diff --git a/.gitignore b/.gitignore index 7e451d1d5..298e62bb8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,34 @@ harbor make/common/config/* +make/dev/adminserver/harbor_adminserver make/dev/ui/harbor_ui make/dev/jobservice/harbor_jobservice +src/adminserver/adminserver src/ui/ui src/jobservice/jobservice +src/common/dao/dao.test *.pyc jobservice/test + +src/ui/static/*.html +src/ui/static/*.bundle.js +src/ui/static/*.bundle.js.map +src/ui/static/harbor-logo.*.png +src/ui/static/i18n/lang/en-us-lang.json +src/ui/static/i18n/lang/zh-cn-lang.json + +src/ui_ng/coverage/ +src/ui_ng/dist/ +src/ui_ng/html-report/ +src/ui_ng/node_modules/ +src/ui_ng/typings/ +**/*npm-debug.log.* +**/*yarn-error.log.* +.idea/ +.DS_Store +**/node_modules +**/ssl/ +**/proxy.config.json +**/npm*.log +**/*ngsummary.json +**/*ngfactory.ts diff --git a/.travis.yml b/.travis.yml index 3ba4b3f55..df904d80a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: true language: go go: - - 1.6.2 + - 1.7.3 go_import_path: github.com/vmware/harbor @@ -13,25 +13,25 @@ services: dist: trusty env: - DB_HOST: 127.0.0.1 - DB_PORT: 3306 - DB_USR: root - DB_PWD: root123 MYSQL_HOST: localhost MYSQL_PORT: 3306 MYSQL_USR: root MYSQL_PWD: root123 + MYSQL_DATABASE: registry + SQLITE_FILE: /tmp/registry.db + ADMIN_SERVER_URL: http://127.0.0.1:8888 DOCKER_COMPOSE_VERSION: 1.7.1 HARBOR_ADMIN: admin HARBOR_ADMIN_PASSWD: Harbor12345 UI_SECRET: tempString MAX_JOB_WORKERS: 3 - SECRET_KEY: 1234567890123456 AUTH_MODE: db_auth - SELF_REGISTRATION: "on" + SELF_REGISTRATION: on + KEY_PATH: /data/secretkey before_install: - sudo ./tests/hostcfg.sh + - sudo ./tests/generateCerts.sh - sudo ./make/prepare install: @@ -70,28 +70,40 @@ install: before_script: # create tables and load data # - mysql < ./make/db/registry.sql -uroot --verbose - - sudo sqlite3 /registry.db < make/common/db/registry_sqlite.sql + - sudo sqlite3 /tmp/registry.db < make/common/db/registry_sqlite.sql + - sudo chmod 777 /tmp/registry.db script: + - sudo mkdir -p /etc/ui/ca/ + - sudo mv ./tests/ca.crt /etc/ui/ca/ + - sudo mkdir -p /harbor + - sudo mv ./VERSION /harbor/VERSION - sudo service mysql stop - sudo ./tests/testprepare.sh - - docker-compose -f ./make/docker-compose.test.yml up -d + - sudo docker-compose -f ./make/docker-compose.test.yml up -d - go list ./... | grep -v -E 'vendor|tests' | xargs -L1 fgt golint - go list ./... | grep -v -E 'vendor|tests' | xargs -L1 go vet - export MYSQL_HOST=$IP - export REGISTRY_URL=$IP:5000 - echo $REGISTRY_URL - - ./tests/pushimage.sh - - ./tests/coverage4gotest.sh + - ./tests/pushimage.sh + - cd tests + - sudo ./ldapprepare.sh + - cd .. + - go test -i ./src/ui ./src/adminserver ./src/jobservice + - sudo -E env "PATH=$PATH" ./tests/coverage4gotest.sh - goveralls -coverprofile=profile.cov -service=travis-ci - docker-compose -f make/docker-compose.test.yml down - - - docker-compose -f make/dev/docker-compose.yml up -d + - sudo rm -rf /data/config/* + - ls /data/cert + - sudo make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:0.8.4 NOTARYFLAG=true - docker ps - - go run tests/startuptest.go http://localhost/ - - go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD} + - ./tests/notarytest.sh + - ./tests/swaggerchecker.sh + - ./tests/startuptest.sh + - ./tests/userlogintest.sh ${HARBOR_ADMIN} ${HARBOR_ADMIN_PASSWD} # - sudo ./tests/testprepare.sh # - go test -v ./tests/apitests diff --git a/AUTHORS b/AUTHORS index 22700ab5e..7a86407e2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,6 +5,7 @@ Alexey Erkak Allen Heavey Amanda Zhang Andre Cruz +Aron Parsons Benniu Ji Bin Liu Bobby Zhang @@ -12,6 +13,7 @@ Brian Christner Chaofeng Wu Daniel Jiang Deshi Xiao +Feileng Cui Guangping Fu Haining Henry Zhang Hao Xia @@ -19,6 +21,7 @@ Haoyuan Jack Liu Jessy Zhang Jianye Li +Kira Kun Wang Mahesh Paolini-Subramanya Maxwell <710028463 at qq.com> @@ -29,6 +32,7 @@ Penghao Cen Phillip Gomez Robin Naundorf Shan Zhu +Steven Zou Robin Yue Tobe Chen Victoria Zheng diff --git a/CHANGELOG.md b/CHANGELOG.md index 5884b6fc9..3fb603ff3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog + +## v1.1.0 (2017-4-18) + +- Add in Notary support +- User can update configuration through Harbor UI +- Redesign of Harbor's UI using Clarity +- Some changes to API +- Fix some security issues in token service +- Upgrade base image of nginx for latest openssl version +- Various bug fixes. + +## v0.5.0 (2016-12-6) + +- Refactory for a new build process +- Easier configuration for HTTPS in prepare script +- Script to collect logs of a Harbor deployment +- User can view the storage usage (default location) of Harbor. +- Add an attribute to disable normal user to create project +- Various bug fixes. + +For Harbor virtual appliance: + +- Improve the bootstrap process of ova installation. +- Enable HTTPS by default for .ova deployment, users can download the default root cert from UI for docker client or VCH. +- Preload a photon:1.0 image to Harbor for users who have no internet connection. + + + ## v0.4.5 (2016-10-31) - Virtual appliance of Harbor for vSphere. diff --git a/LICENSE b/LICENSE index 4ca4ffa06..7ea670095 100644 --- a/LICENSE +++ b/LICENSE @@ -1,10 +1,10 @@ LICENSE -Harbor version 0.5.0 GA +Harbor version 1.1.0 GA -Copyright 2016 VMware, Inc. All rights reserved. +Copyright 2017 VMware, Inc. All rights reserved. -This product is licensed to you under the Apache License version 2.0 (the License). You may not use this product except in compliance with the License. +This product is licensed to you under the Apache License version 2.0 (the “License”). You may not use this product except in compliance with the License. Apache License Version 2.0, January 2004 @@ -210,7 +210,7 @@ limitations under the License. ====================================================================== -Harbor version 0.5.0 includes a number of components with separate copyright notices and license terms. This product does not necessarily use all the open source components referred to below. Your use of the source code for these components is subject to the terms and conditions of the following licenses. +Harbor version 1.1.0 includes a number of components with separate copyright notices and license terms. This product does not necessarily use all the open source components referred to below. Your use of the source code for these components is subject to the terms and conditions of the following licenses. ======================== TABLE OF CONTENTS ============================ @@ -223,18 +223,21 @@ of the license associated with each component. SECTION 1: BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES >>> alembic-0.8.8 - >>> angular-cookies-1.5.3 - >>> angular-messages-1.5.3 - >>> angularjs-1.5.3 - >>> bootstrap-3.3.6 - >>> bootstrap-datepicker-4.17.37 + >>> angular2-2.4.7 + >>> angular2-cookie-1.2.6 + >>> clarity-ui-0.8.6 >>> cronie-1.5.0 - >>> jquery-1.11.2 + >>> dgrijalva/jwt-go-d2709f9f1f31ebcda9651b03077758c1f3a0018c + >>> go-ldap/ldap-v2.5.0 + >>> gorilla/handlers-13d73096a474cac93275c679c7b8a2dc17ddba82 + >>> gorilla/mux-780415097119f6f61c55475fe59b66f3c3e9ea53 + >>> jquery-2.4.4 >>> mattn/go-sqlite3-3fb7a0e7 - >>> moment-2.11.0 - >>> mqu/openldap-0.2 >>> nginx-1.11.5 + >>> ngx-translate-v6.0.0 + >>> ngx-translate/http-loader-0.0.3 >>> shadow-4.2.1 + >>> stretchr/testify-v1.1.4 @@ -242,15 +245,19 @@ SECTION 2: Apache License, V2.0 >>> apr-util-1.5.4 >>> beego-1.6.1 - >>> docker-distribution-2.5.0 >>> docker-libtrust -9cbd2a1 - >>> registry-2.5.0 + >>> docker/distribution-2.6.0 + >>> docker/notary-c04e3e6d05881045def11167c51d4a8baa34899a + >>> opencontainers/go-digest-aa2ec055abd10d26d539eb630a92241b781ce4bc + >>> registry-2.6.0 + >>> rxjs-5.1.1 SECTION 3: GNU General Public License, V2.0 >>> logrotate-3.9.1 + >>> mariadb-10.1.10 >>> mysql-5.6 @@ -269,10 +276,10 @@ SECTION 5: Mozilla Public License, V2.0 APPENDIX. Standard License Files - >>> Mozilla Public License, V2.0 - >>> Apache License, V2.0 + >>> Mozilla Public License, V2.0 + >>> GNU General Public License, V3.0 >>> GNU General Public License, V2.0 @@ -283,8 +290,11 @@ APPENDIX. Standard License Files >>> Creative Commons Attribution License, V3.0 - >>> Common Development and Distribution License, V1.1 + >>> SIL OPEN FONT LICENSE, V1.1 + >>> Creative Commons Attribution-ShareAlike, V4.0 + + >>> Common Development and Distribution License, V1.1 --------------- SECTION 1: BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES ---------- @@ -316,86 +326,11 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR DEALINGS IN THE SOFTWARE. ->>> angular-cookies-1.5.3 +>>> angular2-2.4.7 The MIT License -Copyright (c) 2010-2015 Google, Inc. http://angularjs.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - ->>> angular-messages-1.5.3 - -The MIT License - -Copyright (c) 2010-2015 Google, Inc. http://angularjs.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - ->>> angularjs-1.5.3 - -The MIT License - -Copyright (c) 2010-2015 Google, Inc. http://angularjs.org - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - ->>> bootstrap-3.3.6 - -The MIT License (MIT) - -Copyright (c) 2011-2015 Twitter, Inc +Copyright (c) 2014-2017 Google, Inc. http://angular.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -417,45 +352,18 @@ THE SOFTWARE. ADDITIONAL LICENSE INFORMATION: -> CC- Attribution 3.0 Unported License +> ISC -[NOTE: VMware does not distribute the "docs\assets\css\src\docs.css" subpackage.] +angular-2.4.7.tar.gz\angular-2.4.7.tar\angular-2.4.7\modules\rollup-test\package.json -bootstrap-3.3.6\docs\assets\css \src\docs.css - -Bootstrap Docs (http://getbootstrap.com) -Copyright 2011-2015 Twitter, Inc. -Licensed under the Creative Commons Attribution 3.0 Unported License. For -details, see https://creativecommons.org/licenses/by/3.0/. +License: ISC -> MIT - -[PLEASE NOTE: VMWARE, INC. ELECTS TO USE AND DISTRIBUTE THIS COMPONENT UNDER THE TERMS OF THE MIT LICENSE. THE ORIGINAL LICENSE TERMS ARE REPRODUCED BELOW ONLY AS A REFERENCE.] - -bootstrap-3.3.6.tar.gz\bootstrap-3.3.6.tar\bootstrap-3.3.6\docs\assets\js\vendor\ jszip.min.js - -(c) 2009-2014 Stuart Knightley -Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. - - -> Apache 2.0 - -bootstrap-3.3.6.tar.gz\bootstrap-3.3.6.tar\bootstrap-3.3.6\docs\assets\js\vendor\ less.min.js - -Copyright (c) 2009-2014, Alexis Sellier -Licensed under the Apache v2 License. - -License for GLYPHICONS Halflings in Bootstrap - -GLYPHICONS Halflings font is also released as an extension of a Bootstrap www.getbootstrap.com for free and it is released under the same license as Bootstrap. While you are not required to include attribution on your Bootstrap-based projects, I would certainly appreciate any form of support, even a nice Tweet is enough. Of course if you want, you can say thank you and support me by buying more icons on GLYPHICONS.com. - - ->>> bootstrap-datepicker-4.17.37 +>>> angular2-cookie-1.2.6 The MIT License (MIT) -Copyright (c) 2015 Jonathan Peterson (@Eonasdan) +Copyright (c) 2016 Samet Alemdar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -476,6 +384,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +>>> clarity-ui-0.8.6 + +Copyright 2016 VMware, Inc. All rights reserved + +The MIT license (the "License") set forth below applies to all parts of the Clarity project except for the fonts which are licensed under the SIL Open Font License version 1.1. You may not use this file except in compliance with the License. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +ADDITIONAL LICENSER INFORMATION: + +> SIL Open Font License + +clarity-0.8.6.tar.gz\clarity-0.8.6.tar\clarity-0.8.6\ Clarity_NOTICE.txt + +Copyright (c) 2016 VMware, Inc. All Rights Reserved. + +Except for the fonts, which are licensed under the SIL Open Font License version 1.1 (the "OFL License"), this product is licensed to you under the MIT license (the "MIT License"). You may not use this product except in compliance with the OFL and MIT Licenses. + +This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. + + >>> cronie-1.5.0 Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") @@ -581,10 +517,123 @@ cronie-1.5.0.tar.gz\cronie-1.5.0.tar\cronie-1.5.0\anacron\global.h `COPYING' that comes with the Anacron source distribution. ->>> jquery-1.11.2 +>>> dgrijalva/jwt-go-d2709f9f1f31ebcda9651b03077758c1f3a0018c -Copyright 2014 jQuery Foundation and other contributors -http://jquery.com/ +Copyright (c) 2012 Dave Grijalva + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +>>> go-ldap/ldap-v2.5.0 + +The MIT License (MIT) + +Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com) +Portions copyright (c) 2015-2016 go-ldap Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +ADDITIONAL LICENSE INFORMATION: + +> BSD-Style + +ldap-2.5.0.tar.gz\ldap-2.5.0.tar\ldap-2.5.0\dn.go + +Copyright 2015 The Go Authors. All rights reserved. +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file. + + +>>> gorilla/handlers-13d73096a474cac93275c679c7b8a2dc17ddba82 + +Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +>>> gorilla/mux-780415097119f6f61c55475fe59b66f3c3e9ea53 + +Copyright (c) 2012 Rodrigo Moraes. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +>>> jquery-2.4.4 + +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery + +The following license applies to all parts of this software except as +documented below: + +==== Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -605,6 +654,55 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +ADDITIONAL LICENSE INFORMATION: + +>MIT + +[PLEASE NOTE: VMWARE, INC. ELECTS TO USE AND DISTRIBUTE THIS COMPONENT UNDER THE TERMS OF THE [MIT LICENSE]. THE ORIGINAL LICENSE TERMS ARE REPRODUCED BELOW ONLY AS A REFERENCE.] + + +jquery-2.2.4.tar.gz\jquery-2.2.4.tar\jquery-2.2.4\external\requirejs\require.js + + +license RequireJS 2.1.15 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. +Available via the MIT or new BSD license. +see: http://github.com/jrburke/requirejs for details. + +>BSD 3 Clause + +jquery-2.2.4.tar.gz\jquery-2.2.4.tar\jquery-2.2.4\external\sinon\sinon-1.14.1.js + +author Christian Johansen (christian@cjohansen.no) +author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS + +(The BSD License) + +Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +Neither the name of Christian Johansen nor the names of his contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + >>> mattn/go-sqlite3-3fb7a0e7 @@ -677,58 +775,6 @@ May you find forgiveness for yourself and forgive others. May you share freely, never taking more than you give. ->>> moment-2.11.0 - -Copyright (c) 2011-2015 Tim Wood, Iskren Chernev, Moment.js contributors - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - - ->>> mqu/openldap-0.2 - -/This work is part of OpenLDAP Software . - -Copyright 1998-2012 The OpenLDAP Foundation. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted only as authorized by the OpenLDAP -Public License. - -A copy of this license is available in file LICENSE in the -top-level directory of the distribution or, alternatively, at -. -/ -/ Portions Copyright (c) 1990 Regents of the University of Michigan. -All rights reserved. - -Redistribution and use in source and binary forms are permitted -provided that this notice is preserved and that due credit is given -to the University of Michigan at Ann Arbor. The name of the University -may not be used to endorse or promote products derived from this -software without specific prior written permission. This software -is provided ``as is'' without express or implied warranty. - - >>> nginx-1.11.5 Copyright (C) 2002-2016 Igor Sysoev @@ -757,6 +803,52 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +>>> ngx-translate-v6.0.0 + +Copyright (c) 2016 Olivier Combe + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +ADDITIONAL LICENSE INFORMATION: + +> Apache 2.0 + +core-6.0.0.tar.gz\core-6.0.0.tar\core-6.0.0\examples\ionic2\hooks\README.md + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + + +>>> ngx-translate/http-loader-0.0.3 + +Copyright (c) 2016 Olivier Combe + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + >>> shadow-4.2.1 Copyright (c) 1991 - 1994, Julianne Frances Haugh @@ -969,6 +1061,67 @@ man pages are NOT obsolete! TH su 1 "18 August 1999" "GNU Shell Utilities 20" +>>> stretchr/testify-v1.1.4 + +Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell + +Please consider promoting this project if you find it useful. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +ADDITIONAL LICENSE INFORMATION: + +> BSD-3 + +testify-1.1.4.tar.gz\testify-1.1.4.tar\testify-1.1.4\vendor\github.com\pmezard\go-difflib\LICENSE + +Copyright (c) 2013, Patrick Mezard +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + The names of its contributors may not be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + --------------- SECTION 2: Apache License, V2.0 ---------- Apache License, V2.0 is applicable to the following component(s). @@ -1154,43 +1307,378 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ->>> docker-distribution-2.5.0 +>>> docker-libtrust -9cbd2a1 -License +Copyright and license + +Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. +Docs released under Creative commons. + + +>>> docker/distribution-2.6.0 + +LICENSE: Apache 2.0 + + +>>> docker/notary-c04e3e6d05881045def11167c51d4a8baa34899a + +Copyright 2014 Alan Shreve + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +ADDITIONAL LICENSE INFORMATION: + +> BSD-3 + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\tuf\LICENSE + +Copyright (c) 2015, Docker Inc. +Copyright (c) 2014-2015 Prime Directive, Inc. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Prime Directive, Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +> Public Domain + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\github.com\agl\ed25519\ed25519.go + + This code is a port of the public domain, "ref10" implementation of ed25519 + from SUPERCOP. + + +> Zlib License (MIT-Style) + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\github.com\bugsnag\osext\LICENSE + +Copyright (c) 2012 Daniel Theophanes + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + + +> MIT + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\github.com\bugsnag\panicwrap\LICENSE + +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +> WTFPL 2.0 + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\github.com\BurntSushi\toml\COPYING + + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. You just DO WHAT THE FUCK YOU WANT TO. + + +> MPL 2.0 + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\github.com\go-sql-driver\mysql\infile.go + +Go MySQL Driver - A MySQL-Driver for Go's database/sql package + +Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this file, +You can obtain one at http://mozilla.org/MPL/2.0/. + + +> BSD-2 + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\github.com\magiconair\properties\LICENSE + +goproperties - properties file decoder for Go + +Copyright (c) 2013-2014 - Frank Schroeder + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +> BSD-3 with Google Patent grant + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\golang.org\x\crypto\LICENSE + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +> LGPL 3.0 + +*****VMware does not distribute the sub-components licensed under LGPL 3.0***** + + +notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar.gz\notary-c04e3e6d05881045def11167c51d4a8baa34899a.tar\notary-c04e3e6d05881045def11167c51d4a8baa34899a\vendor\gopkg.in\yaml.v2\LICENSE + +Copyright (c) 2011-2014 - Canonical Inc. + +This software is licensed under the LGPLv3, included below. + +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. + +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. + + +>>> opencontainers/go-digest-aa2ec055abd10d26d539eb630a92241b781ce4bc + +Copyright 2017 Docker, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +ADDITIONAL LICENSE INFORMATION: + +> CC-Attribution-ShareAlike 4.0 + +go-digest-master.zip\go-digest-master\LICENSE.docs + +License: CC-Attribution-ShareAlike 4.0 + + +>>> registry-2.6.0 + +AWS SDK for Go +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2014-2015 Stripe, Inc. +Copyright 2014 Unknwon + +Licensed under the Apache License, Version 2.0 (the "License"): you may +not use this file except in compliance with the License. You may obtain +a copy of the License at + +http:www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations +under the License. -This project is distributed under [Apache License, Version 2.0](LICENSE). ADDITIONAL LICENSE INFORMATION: > MIT -distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\bugsnag\bugsnag-go\LICENSE.txt +distribution-2.6.0.tar.gz\distribution-2.6.0.tar\distribution-2.6.0\registry\storage\driver\s3-aws\s3_v2_signer.go -Copyright (c) 2014 Bugsnag +Copyright (c) 2013 Damien Le Berrigaud and Nick Wade -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, andor sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +Copyright (c) 2013 Damien Le Berrigaud and Nick Wade + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, andor sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE., +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. > MIT Style -distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\bugsnag\osext\LICENSE +distribution-2.6.0.tar.gz\distribution-2.6.0.tar\distribution-2.6.0\vendor\github.com\bugsnag\osext\LICENSE Copyright (c) 2012 Daniel Theophanes @@ -1213,85 +1701,45 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. +> BSD Style -> CC-Attribution 3.0 +distribution-2.6.0.tar.gz\distribution-2.6.0.tar\distribution-2.6.0\vendor\github.com\bugsnag\osext\osext.go -distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\docker\libtrust\README.md +Copyright 2012 The Go Authors. All rights reserved. +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file. -Copyright and license +> LGPL3.0 -Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. -Docs released under Creative commons. +*****VMware does not distribute the sub-components licensed under LGPL 3.0***** +distribution-2.6.0.tar.gz\distribution-2.6.0.tar\distribution-2.6.0\vendor\github.com\docker\goamz\LICENSE -> BSD 3-Clause +This software is licensed under the LGPLv3, included below. -distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\golang\protobuf\LICENSE +As a special exception to the GNU Lesser General Public License version 3 +("LGPL3"), the copyright holders of this Library give you permission to +convey to a third party a Combined Work that links statically or dynamically +to this Library without providing any Minimal Corresponding Source or +Minimal Application Code as set out in 4d or providing the installation +information set out in section 4e, provided that you comply with the other +provisions of LGPL3 and provided that you meet, for the Application the +terms and conditions of the license(s) which apply to the Application. -Go support for Protocol Buffers - Google's data interchange format +Except as stated in this special exception, the provisions of LGPL3 will +continue to comply in full to this Library. If you modify this Library, you +may apply this exception to your version of this Library, but you are not +obliged to do so. If you do not wish to do so, delete this exception +statement from your version. This exception does not (and cannot) modify any +license terms which apply to the Application, with which you must still +comply. -Copyright 2010 The Go Authors. All rights reserved. -https://github.com/golang/protobuf +> BSD 3 Clause -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +distribution-2.6.0.tar.gz\distribution-2.6.0.tar\distribution-2.6.0\vendor\golang.org\x\crypto\LICENSE -* Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. -* Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -> BSD 2-Clause - -distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\gorilla\handlers\LICENSE - -Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -> BSD-3 - -distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\golang.org\x\crypto\LICENSE - -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2011 The Go Authors. All rights reserved. +https:github.com/golang/protobuf Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -1343,17 +1791,38 @@ rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. ->>> docker-libtrust -9cbd2a1 +> BSD 2 Clause -Copyright and license - -Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. -Docs released under Creative commons. +distribution-2.6.0.tar.gz\distribution-2.6.0.tar\distribution-2.6.0\vendor\github.com\gorilla\handlers\proxy_headers.go ->>> registry-2.5.0 +Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. -License: Apache 2.0 +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +>>> rxjs-5.1.1 + +License: Apache License 2.0 --------------- SECTION 3: GNU General Public License, V2.0 ---------- @@ -1399,6 +1868,706 @@ Copyright (c) 1991, 1993 SUCH DAMAGE. +>>> mariadb-10.1.10 + +Copyright 2001, 2012, Oracle and/or its affiliates. +Copyright 2009, 2016, MariaDB + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version of the License. This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +ADDITIONAL LICENSE INFORMATION: + +>BSD-3 Clause +Cmake +Use of any of this software is governed by the terms of the license below: + +CMake is distributed under BSD License + +Copyright (c) 2008, Kitware, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of Kitware, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY Kitware, Inc. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Kitware Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>MIT + +cmake-2.4.8/Utilities/cmtar/compat/gethostname.c: +gethostname.c: minimal substitute for missing gethostname() function +created 2000-Mar-02 jmk +requires SVR4 uname() and -lc + +by Jim Knoble +Copyright ? 2000 Jim Knoble + +Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. + +This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the author(s) be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software. + +>Public Domain +Originally written by Steven M. Bellovin while at the University of North Carolina at Chapel Hill. Later tweaked by a couple of people on Usenet. Completely overhauled by Rich $alz and Jim Berets in August, 1990. + +This code is in the public domain and has no copyright. + +>MIT-Style +THIS CODE IS SPECIFICALLY EXEMPTED FROM THE NCURSES PACKAGE COPYRIGHT. You may freely copy it for use as a template for your own field types. If you develop a field type that might be of general use, please send it back to the ncurses maintainers for inclusion in the next version. + + ************************************************** + * Author : Per Foreby, perf@efd.lth.se + * Author : Juergen Pfeifer, juergen.pfeifer@gmx.net + + ************************************************** + +>MIT-Style + +Copyright (c) 2002 Insight Consortium. All rights reserved. +See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + +This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. + +>GPL v2 with Bison Exception +Skeleton parser for Yacc-like parsing with Bison, +Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +As a special exception, when this file is copied by Bison into a Bison output file, you may use that output file without restriction. This special exception was added by the Free Software Foundation in version 1.24 of Bison. + +>BSD-Style +cmake-2.4.8/Utilities/cmzlib/zlib.h: +zlib.h -- interface of the 'zlib' general purpose compression library version 1.1.4, March 11th, 2002 + +Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler +>BSD-3 Clause +This source code was modified by Martin Hedenfalk for use in Curl. His latest changes were done 2000-09-18. + +It has since been patched away like a madman by Daniel Stenberg to make it better applied to curl conditions, and to make it not use globals, pollute name space and more. This source code awaits a rewrite to work around the paragraph 2 in the BSD licenses as explained below. + +Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Hgskolan +It has since been patched and modified a lot by Daniel Stenberg to make it better applied to curl conditions, and to make it not use globals, pollute name space and more. This source code awaits a rewrite to work around the paragraph 2 in the BSD licenses as explained below. + +Copyright (c) 1998, 1999 Kungliga Tekniska Hgskolan (Royal Institute of Technology, Stockholm, Sweden). +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the Institute nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +>MIT +cmake-2.4.8/Utilities/cmcurl/inet_pton.c, +cmake-2.4.8/Source/CTest/Curl/inet_pton.c: +This is from the BIND 4.9.4 release, modified to compile by itself + +Copyright (c) 1996 by Internet Software Consortium. + +Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +>BSD-3 Clause +Copyright (C) 2001 by Eric Kidd. All rights reserved. +Copyright (C) 2001 by Luke Howard. All rights reserved. +Copyright (C) 2002 Ximian, Inc. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>BSD-3 Clause +Copyright (c) 1994 +The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by +Chuck Karish of Mindcraft, Inc. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + +Copyright (c) 1985, 1986 The Regents of the University of California. +All rights reserved. + +This code is derived from software contributed to Berkeley by James A. Woods, derived from original work by Spencer Thomas and Joseph Orost. + +>BSD 4-Clause +Copyright (c) 1989, 1993, 1994 +The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by Guido van Rossum. + +Copyright (c) 1990 The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software must display the following acknowledgement: + This product includes software developed by the University of California, Berkeley and its contributors. +4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>MIT + Project ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. +Copyright (C) 2004, Daniel Stenberg, , et al. + +This software is licensed as described in the file COPYING, which you should have received as part of this distribution. The terms are also available at http://curl.haxx.se/docs/copyright.html. + +Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. + +You may opt to use, copy, modify, merge, publish, distribute and/or sell copies of the Software, and permit persons to whom the Software is furnished to do so, under the terms of the COPYING file. + +This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + +>MIT +Copyright (c) 1998 Free Software Foundation, Inc. +Copyright (c) 1998,2000 Free Software Foundation, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, distribute with modifications, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization. + +>BSD-3 Clause +Copyright (c) 1997 Todd C. Miller +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>Public Domain +%%The following software may be included in this product: Fred Fish's Dbug Library + +Use of any of this software is governed by the terms of the license below: + + * N O T I C E * + * * + * Copyright Abandoned, 1987, Fred Fish * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + * + +>Public Domain +%%The following software may be included in this product: dbug_analyze.c (part of Fred Fish's Dbug Library) + +Use of any of this software is governed by the terms of the license below: + + * Copyright Abandoned, 1987, Fred Fish * + * * + * This previously copyrighted work has been placed into the public * + * domain by the author and may be freely used for any purpose, * + * private or commercial. * + * * + * Because of the number of inquiries I was receiving about the use * + * of this product in commercially developed works I have decided to * + * simply make it public domain to further its unrestricted use. I * + * specifically would be most happy to see this material become a * + * part of the standard Unix distributions by AT&T and the Berkeley * + * Computer Science Research Group, and a standard part of the GNU * + * system from the Free Software Foundation. * + * * + * I would appreciate it, as a courtesy, if this notice is left in * + * all copies and derivative works. Thank you. * + * * + * The author makes no warranty of any kind with respect to this * + * product and explicitly disclaims any implied warranties of mer- * + * chantability or fitness for any particular purpose. * + +>GNU GPL v2 +innochecksum.c + +Use of any of this software is governed by the terms of the license below: + +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +[THE TEXT OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 IS SET FORTH IN THE APPENDIX OF THIS DOCUMENT.] + +>MIT-Style +%%The following software may be included in this product: lib_sql.cc + +Use of any of this software is governed by the terms of the license below: + +Copyright (c) 2000 +SWsoft company + +This material is provided "as is", with absolutely no warranty expressed or implied. Any use is at your own risk. + +Permission to use or copy this software for any purpose is hereby granted without fee, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, provided the above notices are retained, and a notice that the code was modified is included with the above copyright notice. + +This code was modified by the MySQL team + +>Public Domain +%%The following software may be included in this product: +Richard A. O'Keefe strings package + +Use of any of this software is governed by the terms of the license below: + +These files are in the public domain. This includes getopt.c, which is the work of Henry Spencer, University of Toronto Zoology, who says of it "None of this software is derived from Bell software. I had no access to the source for Bell's versions at the time I wrote it. This software is hereby explicitly placed in the public domain. It may be used for any purpose on any machine by anyone." I would greatly prefer it if *my* material received no military use. + +>MIT-Style +%%The following software may be included in this product: t_ctype.h + +Use of any of this software is governed by the terms of the license below: +http://bioinfo.mbb.yale.edu/genome/yeast/cluster/database/mysql/include/t_ctype.h + +Copyright (C) 1998, 1999 by Pruet Boonma, all rights reserved. +Copyright (C) 1998 by Theppitak Karoonboonyanan, all rights reserved. +Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies. Smaphan Raruenrom and Pruet Boonma makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. + +>Public Domain + +%%The following software may be included in this product: SHA-1 in C + +Use of any of this software is governed by the terms of the license below: + +SHA-1 in C +By Steve Reid +100% Public Domain + +Additional License(s) +100% Public Domain + +>Public Domain +%%The following software may be included in this product: The tz database + +Use of any of this software is governed by the terms of the license below: + +Sources for Time Zone and Daylight Saving Time Data @(#)tz-link.htm 7.54 + +Please send corrections to this web page to the time zone mailing list. The tz database + +The public-domain time zone database contains code and data that represent the history of local time for many representative locations around the globe. It is updated periodically to reflect changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules. This database (often called tz or zoneinfo) is used by several implementations, including the GNU C Library used in GNU/Linux, FreeBSD, NetBSD, OpenBSD, Cygwin, DJGPP, HP-UX, IRIX, Mac OS X, OpenVMS, Solaris, Tru64, and UnixWare. + +Each location in the database represents a national region where all clocks keeping local time have agreed since 1970. Locations are identified by continent or ocean and then by the name of the location, which is typically the largest city within the region. For example, America/New_York represents most of the US eastern time zone; America/Phoenix represents most of Arizona, which uses mountain time without daylight saving time (DST); America/Detroit represents most of Michigan, which uses eastern time but with different DST rules in 1975; and other entries represent smaller regions like Starke County, Indiana, which switched from central to eastern time in 1991 and switched back in 2006. To use the database on an extended POSIX implementation set the TZ environment variable to the location's full name, e.g., TZ="America/New_York". + +In the tz database's FTP distribution the code is in the file tzcodeC.tar.gz, where C is the code's version; similarly, the data are in tzdataD.tar.gz, where D is the data's version. The following shell commands download these files to a GNU/Linux or similar host; see the downloaded README file for what to do next. + + wget 'ftp://elsie.nci.nih.gov/pub/tz*.tar.gz' + gzip -dc tzcode*.tar.gz | tar -xf - + gzip -dc tzdata*.tar.gz | tar -xf - + +The code lets you compile the tz source files into machine-readable binary files, one for each location. It also lets you read a tz binary file and interpret time stamps for that location. + +The data are by no means authoritative. If you find errors, please send changes to the time zone mailing list. You can also subscribe to the mailing list, retrieve the archive of old messages (in gzip compressed format), or retrieve archived older versions of code and data; there is also a smaller HTTP mirror. + +>Unicode License +%%The following software may be included in this product: UnicodeData.txt + +Use of any of this software is governed by the terms of the license below: + +Unicode Terms of Use + +For the general privacy policy governing access to this site, see the Unicode Privacy Policy. For trademark usage, see the Unicode Consortium (R) Trademarks and Logo Policy. +Notice to End User: Terms of Use +Carefully read the following legal agreement ("Agreement"). Use or copying of the software and/or codes provided with this agreement (The "Software") constitutes your acceptance of these terms + +1. Unicode Copyright. + 1. Copyright (c) 1991-2008 Unicode, Inc. All rights reserved. + 2. Certain documents and files on this website contain a legend indicating that "Modification is permitted." Any person is hereby authorized, without fee, to modify such documents and files to create derivative works conforming to the Unicode (R) Standard, subject to Terms and Conditions herein. + 3. Any person is hereby authorized, without fee, to view, use, reproduce, and distribute all documents and files solely for informational purposes in the creation of products supporting the Unicode Standard, subject to the Terms and Conditions herein. + 4. Further specifications of rights and restrictions pertaining to the use of the particular set of data files known as the "Unicode Character Database" can be found in Exhibit 1. + 5. Each version of the Unicode Standard has further specifications of rights and restrictions of use. For the book editions, these are found on the back of the title page. For the online edition, certain files (such as the PDF files for book chapters and code charts) carry specific restrictions. All other files are covered under these general Terms of Use. To request a permission to reproduce any part of the Unicode Standard, please contact the Unicode Consortium. + 6. No license is granted to "mirror" the Unicode website where a fee is charged for access to the "mirror" site. + 7. Modification is not permitted with respect to this document. All copies of this document must be verbatim. + +2. Restricted Rights Legend. Any technical data or software which is licensed to the United States of America, its agencies and/or instrumentalities under this Agreement is commercial technical data or commercial computer software developed exclusively at private expense as defined in FAR 2.101, or DFARS 252.227-7014 (June 1995), as applicable. For technical data, use, duplication, or disclosure by the Government is subject to restrictions as set forth in DFARS 202.227-7015 Technical Data, Commercial and Items (Nov 1995) and this Agreement. For Software, in accordance with FAR 12-212 or DFARS 227-7202, as applicable, use, duplication or disclosure by the Government is subject to the restrictions set forth in this Agreement. + +3. Warranties and Disclaimers. + 1. This publication and/or website may include technical or typographical errors or other inaccuracies . Changes are periodically added to the information herein; these changes will be incorporated in new editions of the publication and/or website. Unicode may make improvements and/or changes in the product(s) and/or program(s) described in this publication and/or website at any time. + 2. If this file has been purchased on magnetic or optical media from Unicode, Inc. the sole and exclusive remedy for any claim will be exchange of the defective media within ninety (90) days of original purchase. + 3. EXCEPT AS PROVIDED IN SECTION C.2, THIS PUBLICATION AND/OR SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND EITHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. UNICODE AND ITS LICENSORS ASSUME NO RESPONSIBILITY FOR ERRORS OR OMISSIONS IN THIS PUBLICATION AND/OR SOFTWARE OR OTHER DOCUMENTS WHICH ARE REFERENCED BY OR LINKED TO THIS PUBLICATION OR THE UNICODE WEBSITE. + +4. Waiver of Damages. In no event shall Unicode or its licensors be liable for any special, incidental, indirect or consequential damages of any kind, or any damages whatsoever, whether or not Unicode was advised of the possibility of the damage, including, without limitation, those resulting from the following: loss of use, data or profits, in connection with the use, modification or distribution of this information or its derivatives. + +5. Trademarks. + 1. Unicode and the Unicode logo are registered trademarks of Unicode, Inc. + 2. This site contains product names and corporate names of other companies. All product names and company names and logos mentioned herein are the trademarks or registered trademarks of their respective owners. Other products and corporate names mentioned herein which are trademarks of a third party are used only for explanation and for the owners' benefit and with no intent to infringe. + 3. Use of third party products or information referred to herein is at the user's risk. + +6. Miscellaneous. + 1. Jurisdiction and Venue. This server is operated from a location in the State of California, United States of America. Unicode makes no representation that the materials are appropriate for use in other locations. If you access this server from other locations, you are responsible for compliance with local laws. This Agreement, all use of this site and any claims and damages resulting from use of this site are governed solely by the laws of the State of California without regard to any principles which would apply the laws of a different jurisdiction. The user agrees that any disputes regarding this site shall be resolved solely in the courts located in Santa Clara County, California. The user agrees said courts have personal jurisdiction and agree to waive any right to transfer the dispute to any other forum. + 2. Modification by Unicode Unicode shall have the right to modify this Agreement at any time by posting it to this site. The user may not assign any part of this Agreement without Unicode's prior written consent. + 3. Taxes. The user agrees to pay any taxes arising from access to this website or use of the information herein, except for those based on Unicode's net income. + 4. Severability. If any provision of this Agreement is declared invalid or unenforceable, the remaining provisions of this Agreement shall remain in effect. + 5. Entire Agreement. This Agreement constitutes the entire agreement between the parties. + +EXHIBIT 1 +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + + Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, and +http://www.unicode.org/cldr/data/ . Unicode Software includes any source code published in the Unicode Standard or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, and +http://www.unicode.org/cldr/data/. + + NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1991-2008 Unicode, Inc. All rights reserved. Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode data files and any associated documentation (the "Data Files") or Unicode software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that (a) the above copyright notice(s) and this permission notice appear with all copies of the Data Files or Software, (b) both the above copyright notice(s) and this permission notice appear in associated documentation, and (c) there is clear notice in each modified Data File or in the Software as well as in the documentation associated with the Data File(s) or Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. + +Unicode and the Unicode logo are trademarks of Unicode, Inc., and may be registered in some jurisdictions. All other trademarks and registered trademarks mentioned herein are the property of their respective owners. + +> BSD-Style +The following software may be included in this product: zlib + +Use of any of this software is governed by the terms of the license below: + +zlib.h -- interface of the 'zlib' general purpose compression library version 1.2.3, July 18th, 2005 + +Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly jloup@gzip.org + Mark Adler madler@alumni.caltech.edu + +> MIT +The following software may be included in this product: dtoa.c + +Use of any of this software is governed by the terms of the license below: + +This file incorporates work covered by the following copyright and permission notice: + +The author of this software is David M. Gay. + +Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + +Permission to use, copy, modify, and distribute this software for any purpose without fee is hereby granted, provided that this entire notice is included in all copies of any software which is or includes a copy or modification of this software and in all copies of the supporting documentation for such software. + +THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + +>GNU General Public License v2 +%%The following software may be included in this product: getarg.{c,h} + +Use of any of this software is governed by the terms of the license below: + +Copyright (C) 2003 MySQL AB + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + +>BSD-3 Clause +Copyright (c) 1997, 1999 Kungliga Tekniska H366gskolan +(Royal Institute of Technology, Stockholm, Sweden). +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the Institute nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF sSUCH DAMAGE. + +>Public Domain +%%The following software may be included in this product: MD5 message-digest algorithm (md5_hash.cpp) + +Use of any of this software is governed by the terms of the license below: + +This code implements the MD5 message-digest algorithm. The algorithm is due to Ron Rivest. This code was written by Colin Plumb in 1993, no copyright is claimed. This code is in the public domain; do with it what you wish. + +Equivalent code is available from RSA Data Security, Inc. This code has been tested against that, and is equivalent, except that you don't need to include two pages of legalese with every copy. + +The code has been modified by Mikael Ronstroem to handle calculating a hash value of a key that is always a multiple of 4 bytes long. Word 0 of the calculated 4-word hash value is returned as the hash value. + +>Public Domain +%%The following software may be included in this product: nt_servc.{cc,h} + +Use of any of this software is governed by the terms of the license below: + +@file + +@brief +Windows NT Service class library. + +Copyright Abandoned 1998 Irena Pancirov - Irnet Snc +This file is public domain and comes with NO WARRANTY of any kind + +>GNU GPL v2 +%%The following software may be included in this product: GNU Readline + +Use of any of this software is governed by the terms of the license below: + +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +[THE TEXT OF THE GNU GPL v2 IS SET FORTH IN THE APPENDIX OF THIS DOCUMENT.] + +>BSD-3Clause +%%The following software may be included in this product: HandlerSocket plugin for MySQL + +Copyright (c) 2010 DeNA Co.,Ltd. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of DeNA Co.,Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY DeNA Co.,Ltd. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DeNA Co.,Ltd. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>BSD-3 Clause +%%The following software may be included in this product: PCRE (Perl-compatible regular expression library) + +THE BASIC LIBRARY FUNCTIONS +--------------------------- +Written by: Philip Hazel +Email local part: ph10 +Email domain: cam.ac.uk + +University of Cambridge Computing Service, Cambridge, England. + +Copyright (c) 1997-2013 University of Cambridge +All rights reserved. + +THE "BSD" LICENCE +----------------- +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of the University of Cambridge nor the name of Google Inc. nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>LGPL v2 + +https://github.com/MariaDB/server/blob/10.1/BUILD/autorun.sh + +Copyright 2005, 2010 Oracle and/or its affiliates. +All rights reserved. + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2 of the License. + +This program 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 Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +>LGPL v2 +https://github.com/MariaDB/server/blob/10.1/client/completion_hash.h + +Copyright 2000-2002, 2006 MySQL AB +Use is subject to license terms + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2 of the License. + +This program 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 Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +>GNU GPL v3 +tianon/gosu (github.com/tianon/gosu) + +>GNU GPL v2 +github.com/percona/percona-xtrabackup +Copyright 2009, 2014, Oracle and/or its affiliates. +All rights reserved. + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITIY or FITNESS FOR OA PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +>Public Domain + +https://github.com/percona/percona-xtrabackup/blob/2.3/scripts/mysqld_safe.sh + +Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB +This file is public domain and comes with NO WARRANTY of any kind + +>LGPL v2 + +https://github.com/percona/percona-xtrabackup/blob/2.3/scripts/mysqlhotcopy.sh + +Copyright 2000, 2010, Oracle and/or its affiliates. All rights reserved. + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2 of the License. + +This program 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 Library General Public License for more details. + +You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation , Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +>BSD-Style + +https://github.com/percona/percona-xtrabackup/blob/2.3/zlib/zlib.h + +Copyright 1995-2005 Jean-loup Gailly and Mark Adler + +This software is provided as-is, without any expres or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu + +>BSD-3 Clause + +https://github.com/percona/percona-xtrabackup/blob/2.3/libevent/WIN32-Code/win32.c + +Copyright 2000-2002 +Niels Provos +BSD-2 Clause + +https://github.com/percona/percona-xtrabackup/blob/2.3/libevent/WIN32-Code/tree.h + +Copyright 2002 Niels Provos +provos@citi.umich.edu +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULIAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>BSD-3 Clause + +https://github.com/percona/percona-xtrabackup/blob/2.3/libevent/event-internal.h +Copyright 2000-2004 Niels Provos +provos@citi.umich.edu +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + + + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAUR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +>BSD-Style + +https://github.com/percona/percona-xtrabackup/blob/2.3/regex/COPYRIGHT + +Copyright 1992, 1993, 1994 Henry Spencer. +All rights reserved. + +This software is not subject to any license of the American Telephone and Telegraph Company or of the Regents of the University of California. + +Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it, subject to the following restrictions: + +1. The author is not responsible for the consequences of use of this software, no matter how awful, even if they arise from flaws in it. + +2. The origin of this software must not be misrepresented, either by explicit claim or by omission. Since few users ever read sources, credits must appear in the documentation. + +3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. Since few users ever read sources, credits must appear in the documentation. + +4. This notice may not be removed or altered. + + >>> mysql-5.6 License: GPL 2.0 @@ -1637,7 +2806,185 @@ You can obtain one at http://mozilla.org/MPL/2.0/. ---------------- SECTION 1: Mozilla Public License, V2.0 ----------- +--------------- SECTION 1: Apache License, V2.0 ----------- + +Apache License + +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the +copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other +entities that control, are controlled by, or are under common control +with that entity. For the purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or management +of such entity, whether by contract or otherwise, or (ii) ownership +of fifty percent (50%) or more of the outstanding shares, or (iii) +beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled +object code, generated documentation, and conversions to other media +types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a copyright +notice that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this +License, Derivative Works shall not include works that remain separable +from, or merely link (or bind by name) to the interfaces of, the Work +and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the +original version of the Work and any modifications or additions to +that Work or Derivative Works thereof, that is intentionally submitted +to Licensor for inclusion in the Work by the copyright owner or by an +individual or Legal Entity authorized to submit on behalf of the copyright +owner. For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or its +representatives, including but not limited to communication on electronic +mailing lists, source code control systems, and issue tracking systems +that are managed by, or on behalf of, the Licensor for the purpose of +discussing and improving the Work, but excluding communication that is +conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. +Subject to the terms and conditions of this License, each Contributor +hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and +distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. +Subject to the terms and conditions of this License, each Contributor +hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, +royalty- free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and +otherwise transfer the Work, where such license applies only to those +patent claims licensable by such Contributor that are necessarily +infringed by their Contribution(s) alone or by combination of +their Contribution(s) with the Work to which such Contribution(s) +was submitted. If You institute patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted +to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. +You may reproduce and distribute copies of the Work or Derivative Works +thereof in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + a. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + + b. You must cause any modified files to carry prominent notices stating + that You changed the files; and + + c. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + + d. If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one of + the following places: within a NOTICE text file distributed as part + of the Derivative Works; within the Source form or documentation, + if provided along with the Derivative Works; or, within a display + generated by the Derivative Works, if and wherever such third-party + notices normally appear. The contents of the NOTICE file are for + informational purposes only and do not modify the License. You + may add Your own attribution notices within Derivative Works that + You distribute, alongside or as an addendum to the NOTICE text + from the Work, provided that such additional attribution notices + cannot be construed as modifying the License. You may add Your own + copyright statement to Your modifications and may provide additional + or different license terms and conditions for use, reproduction, or + distribution of Your modifications, or for any such Derivative Works + as a whole, provided Your use, reproduction, and distribution of the + Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. +Unless You explicitly state otherwise, any Contribution intentionally +submitted for inclusion in the Work by You to the Licensor shall be +under the terms and conditions of this License, without any additional +terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may +have executed with Licensor regarding such Contributions. + +6. Trademarks. +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. +Unless required by applicable law or agreed to in writing, Licensor +provides the Work (and each Contributor provides its Contributions) on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied, including, without limitation, any warranties or +conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. +In no event and under no legal theory, whether in tort (including +negligence), contract, or otherwise, unless required by applicable law +(such as deliberate and grossly negligent acts) or agreed to in writing, +shall any Contributor be liable to You for damages, including any direct, +indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to +use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other +commercial damages or losses), even if such Contributor has been advised +of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. +While redistributing the Work or Derivative Works thereof, You may +choose to offer, and charge a fee for, acceptance of support, warranty, +indemnity, or other liability obligations and/or rights consistent with +this License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf of +any other Contributor, and only if You agree to indemnify, defend, and +hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such +warranty or additional liability. + +END OF TERMS AND CONDITIONS + + +--------------- SECTION 2: Mozilla Public License, V2.0 ----------- Mozilla Public License Version 2.0 @@ -1820,185 +3167,6 @@ Exhibit B - Incompatible With Secondary Licenses Notice This Source Code Form is Incompatible With Secondary Licenses, as defined by the Mozilla Public License, v. 2.0. ---------------- SECTION 2: Apache License, V2.0 ----------- - -Apache License - -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, -and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the -copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other -entities that control, are controlled by, or are under common control -with that entity. For the purposes of this definition, "control" means -(i) the power, direct or indirect, to cause the direction or management -of such entity, whether by contract or otherwise, or (ii) ownership -of fifty percent (50%) or more of the outstanding shares, or (iii) -beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation source, -and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation -or translation of a Source form, including but not limited to compiled -object code, generated documentation, and conversions to other media -types. - -"Work" shall mean the work of authorship, whether in Source or -Object form, made available under the License, as indicated by a copyright -notice that is included in or attached to the work (an example is provided -in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, -that is based on (or derived from) the Work and for which the editorial -revisions, annotations, elaborations, or other modifications represent, -as a whole, an original work of authorship. For the purposes of this -License, Derivative Works shall not include works that remain separable -from, or merely link (or bind by name) to the interfaces of, the Work -and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the -original version of the Work and any modifications or additions to -that Work or Derivative Works thereof, that is intentionally submitted -to Licensor for inclusion in the Work by the copyright owner or by an -individual or Legal Entity authorized to submit on behalf of the copyright -owner. For the purposes of this definition, "submitted" means any form of -electronic, verbal, or written communication sent to the Licensor or its -representatives, including but not limited to communication on electronic -mailing lists, source code control systems, and issue tracking systems -that are managed by, or on behalf of, the Licensor for the purpose of -discussing and improving the Work, but excluding communication that is -conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity -on behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work. - -2. Grant of Copyright License. -Subject to the terms and conditions of this License, each Contributor -hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, -royalty-free, irrevocable copyright license to reproduce, prepare -Derivative Works of, publicly display, publicly perform, sublicense, and -distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. -Subject to the terms and conditions of this License, each Contributor -hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, -royalty- free, irrevocable (except as stated in this section) patent -license to make, have made, use, offer to sell, sell, import, and -otherwise transfer the Work, where such license applies only to those -patent claims licensable by such Contributor that are necessarily -infringed by their Contribution(s) alone or by combination of -their Contribution(s) with the Work to which such Contribution(s) -was submitted. If You institute patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Work or a Contribution incorporated within the Work constitutes direct -or contributory patent infringement, then any patent licenses granted -to You under this License for that Work shall terminate as of the date -such litigation is filed. - -4. Redistribution. -You may reproduce and distribute copies of the Work or Derivative Works -thereof in any medium, with or without modifications, and in Source or -Object form, provided that You meet the following conditions: - - a. You must give any other recipients of the Work or Derivative Works - a copy of this License; and - - b. You must cause any modified files to carry prominent notices stating - that You changed the files; and - - c. You must retain, in the Source form of any Derivative Works that - You distribute, all copyright, patent, trademark, and attribution - notices from the Source form of the Work, excluding those notices - that do not pertain to any part of the Derivative Works; and - - d. If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one of - the following places: within a NOTICE text file distributed as part - of the Derivative Works; within the Source form or documentation, - if provided along with the Derivative Works; or, within a display - generated by the Derivative Works, if and wherever such third-party - notices normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. You - may add Your own attribution notices within Derivative Works that - You distribute, alongside or as an addendum to the NOTICE text - from the Work, provided that such additional attribution notices - cannot be construed as modifying the License. You may add Your own - copyright statement to Your modifications and may provide additional - or different license terms and conditions for use, reproduction, or - distribution of Your modifications, or for any such Derivative Works - as a whole, provided Your use, reproduction, and distribution of the - Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. -Unless You explicitly state otherwise, any Contribution intentionally -submitted for inclusion in the Work by You to the Licensor shall be -under the terms and conditions of this License, without any additional -terms or conditions. Notwithstanding the above, nothing herein shall -supersede or modify the terms of any separate license agreement you may -have executed with Licensor regarding such Contributions. - -6. Trademarks. -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. -Unless required by applicable law or agreed to in writing, Licensor -provides the Work (and each Contributor provides its Contributions) on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either -express or implied, including, without limitation, any warranties or -conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR -A PARTICULAR PURPOSE. You are solely responsible for determining the -appropriateness of using or redistributing the Work and assume any risks -associated with Your exercise of permissions under this License. - -8. Limitation of Liability. -In no event and under no legal theory, whether in tort (including -negligence), contract, or otherwise, unless required by applicable law -(such as deliberate and grossly negligent acts) or agreed to in writing, -shall any Contributor be liable to You for damages, including any direct, -indirect, special, incidental, or consequential damages of any character -arising as a result of this License or out of the use or inability to -use the Work (including but not limited to damages for loss of goodwill, -work stoppage, computer failure or malfunction, or any and all other -commercial damages or losses), even if such Contributor has been advised -of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. -While redistributing the Work or Derivative Works thereof, You may -choose to offer, and charge a fee for, acceptance of support, warranty, -indemnity, or other liability obligations and/or rights consistent with -this License. However, in accepting such obligations, You may act only -on Your own behalf and on Your sole responsibility, not on behalf of -any other Contributor, and only if You agree to indemnify, defend, and -hold each Contributor harmless for any liability incurred by, or claims -asserted against, such Contributor by reason of your accepting any such -warranty or additional liability. - -END OF TERMS AND CONDITIONS - - - --------------- SECTION 3: GNU General Public License, V3.0 ----------- GNU GENERAL PUBLIC LICENSE @@ -3019,7 +4187,6 @@ consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. - --------------- SECTION 5: GNU Library General Public License, V2.0 ----------- GNU LIBRARY GENERAL PUBLIC LICENSE @@ -4100,23 +5267,302 @@ NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. +--------------- SECTION 8: SIL OPEN FONT LICENSE, V1.1 -------------- + +SIL OPEN FONT LICENSE + +Version 1.1 - 26 February 2007 + +PREAMBLE + +The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. + +DEFINITIONS + +"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the copyright statement(s). + +"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, or substituting in part or in whole any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. + +5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. + +TERMINATION + +This license becomes null and void if any of the above conditions are not met. + +DISCLAIMER + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. +Standard License Header + +There is no standard license header for the license + + +--------------- SECTION 9: Creative Commons Attribution-ShareAlike, V4.0 -------------- + +Creative Commons Attribution-ShareAlike 4.0 International Public License + +Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. + +Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. + +Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. + +Creative Commons Attribution-ShareAlike 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. + +Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. +Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. +BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. +Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. +Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. +Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. +License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. +Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. +Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. +Licensor means the individual(s) or entity(ies) granting rights under this Public License. +Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. +Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. +You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + +License grant. +Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: +reproduce and Share the Licensed Material, in whole or in part; and +produce, reproduce, and Share Adapted Material. +Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. +Term. The term of this Public License is specified in Section 6(a). +Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. +Downstream recipients. +Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. +Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply. +No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. +No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). + +Other rights. +Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. +Patent and trademark rights are not licensed under this Public License. +To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + +Attribution. + +If You Share the Licensed Material (including in modified form), You must: +retain the following if it is supplied by the Licensor with the Licensed Material: +identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); +a copyright notice; +a notice that refers to this Public License; +a notice that refers to the disclaimer of warranties; +a URI or hyperlink to the Licensed Material to the extent reasonably practicable; +indicate if You modified the Licensed Material and retain an indication of any previous modifications; and +indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. +You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. +If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. +ShareAlike. + +In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. +The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. +You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. +You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + +for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; +if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and +You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. +To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. + +The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + +This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. + +Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: +automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or +upon express reinstatement by the Licensor. +For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. +For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. +Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + +The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. +Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. + +Section 8 – Interpretation. + +For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. +To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. +No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. +Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. + + +--------------- SECTION 10: Common Development and Distribution License, V1.1 -------------- + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)Version 1.1 + +1. Definitions. + +1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications. + +1.2. "Contributor Version" means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. + +1.3. "Covered Softwareƒ" means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. + +1.4. "Executable" means the Covered Software in any form other than Source Code. + +1.5. "Initial Developer" means the individual or entity that first makes Original Software available under this License. + +1.6. "Larger Work" means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. + +1.7. "License" means this document. + +1.8. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. + +1.9. "Modifications" means the Source Code and Executable form of any of the following: +A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; +B. Any new file that contains any part of the Original Software or previous Modification; or +C. Any new file that is contributed or otherwise made available under the terms of this License. + +1.10. "Original Software" means the Source Code and Executable form of computer software code that is originally released under this License. + +1.11. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. + +1.12. "Source Code" means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. + +1.13. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. + +2. License Grants. + +2.1. The Initial Developer Grant. +Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: +(a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and +(b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). +(c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. +(d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices. + +2.2. Contributor Grant. +Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: +(a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and +(b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). +(c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. +(d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. + +3. Distribution Obligations. + +3.1. Availability of Source Code. +Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. + +3.2. Modifications. +The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. + +3.3. Required Notices. +You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. + +3.4. Application of Additional Terms. +You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. + +3.5. Distribution of Executable Versions. +You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. + +3.6. Larger Works. +You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. + +4. Versions of the License. + +4.1. New Versions. +Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. + +4.2. Effect of New Versions. +You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. + +4.3. Modified Versions. +When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. +COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + +6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. + +6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as "Participant") alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. + +6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. + +6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. +The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as that term is defined at 48 C.F.R. sec. 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. + +9. MISCELLANEOUS. +This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. +As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) +The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. + =========================================================================== + To the extent any open source components are licensed under the GPL and/or LGPL, or other similar licenses that require the source code -and/or modifications to source code to be made available (as would be -noted above), you may obtain a copy of the source code corresponding to -the binaries for such open source components and modifications thereto, -if any, (the "Source Files"), by downloading the Source Files from -VMware's website at http://www.vmware.com/download/open_source.html, or -by sending a request, with your name and address to: VMware, Inc., 3401 -Hillview Avenue, Palo Alto, CA 94304, United States of America. All such -requests should clearly specify: OPEN SOURCE FILES REQUEST, Attention -General Counsel. VMware shall mail a copy of the Source Files to you on -a CD or equivalent physical medium. This offer to obtain a copy of the -Source Files is valid for three years from the date you acquired this -Software product. Alternatively, the Source Files may accompany the -VMware product. +and/or modifications to source code to be made available, you may obtain +a copy of the source code corresponding to the binaries for such open source +components and modifications thereto, if any, (the "Source Files"), by downloading the +Source Files from VMware's github site for this product, or by sending a request, with your name +and address to: VMware, Inc., 3401 Hillview Avenue, Palo Alto, CA 94304, United States of America. +All such requests should clearly specify: OPEN SOURCE FILES REQUEST, Attention General Counsel. VMware shall +mail a copy of the Source Files to you on a CD or equivalent physical medium. This offer to obtain a copy of the Source +Files is valid for three years from the date you acquired this software product. -[HARBOR050GASR111116] \ No newline at end of file +[HARBOR110GASR041417] \ No newline at end of file diff --git a/Makefile b/Makefile index c9c3b2f75..6faa7dc65 100644 --- a/Makefile +++ b/Makefile @@ -1,368 +1,452 @@ -# Makefile for Harbor project -# -# Targets: -# -# all: prepare env, compile binarys, build images and install images -# prepare: prepare env -# compile: compile ui and jobservice code -# compile_buildgolangimage: -# compile local building golang image -# forexample : make compile_buildgolangimage -e \ -# GOBUILDIMAGE=harborgo:1.6.2 -# compile_golangimage: -# compile from golang image -# for example: make compile_golangimage -e GOBUILDIMAGE= \ -# harborgo:1.6.2 -# compile_ui, compile_jobservice: compile specific binary -# -# build: build Harbor docker images (defuault: build_photon) -# for example: make build -e BASEIMAGE=photon -# build_photon: build Harbor docker images from photon bsaeimage -# build_ubuntu: build Harbor docker images from ubuntu baseimage -# -# install: include compile binarys, build images, prepare specific \ -# version composefile and startup Harbor instance -# -# start: startup Harbor instance -# -# down: shutdown Harbor instance -# -# package_online: -# prepare online install package -# for example: make package_online -e DEVFLAG=false\ -# REGISTRYSERVER=reg-bj.eng.vmware.com \ -# REGISTRYPROJECTNAME=harborrelease -# -# package_offline: -# prepare offline install package -# -# pushimage: push Harbor images to specific registry server -# for example: make pushimage -e DEVFLAG=false REGISTRYUSER=admin \ -# REGISTRYPASSWORD=***** \ -# REGISTRYSERVER=reg-bj.eng.vmware.com/ \ -# REGISTRYPROJECTNAME=harborrelease -# note**: need add "/" on end of REGISTRYSERVER. If not setting \ -# this value will push images directly to dockerhub. -# make pushimage -e DEVFLAG=false REGISTRYUSER=vmware \ -# REGISTRYPASSWORD=***** \ -# REGISTRYPROJECTNAME=vmware -# -# clean: remove binary, Harbor images, specific version docker-compose \ -# file, specific version tag and online/offline install package -# cleanbinary: remove ui and jobservice binary -# cleanimage: remove Harbor images -# cleandockercomposefile: -# remove specific version docker-compose -# cleanversiontag: -# cleanpackageremove specific version tag -# cleanpackage: remove online/offline install package -# -# other example: -# clean specific version binarys and images: -# make clean -e VERSIONTAG=[TAG] -# note**: If commit new code to github, the git commit TAG will \ -# change. Better use this commond clean previous images and \ -# files with specific TAG. -# By default DEVFLAG=true, if you want to release new version of Harbor, \ -# should setting the flag to false. -# make XXXX -e DEVFLAG=false - -SHELL := /bin/bash -BUILDPATH=$(CURDIR) -MAKEPATH=$(BUILDPATH)/make -MAKEDEVPATH=$(MAKEPATH)/dev -SRCPATH=./src -TOOLSPATH=$(BUILDPATH)/tools -GOBASEPATH=/go/src/github.com/vmware -CHECKENVCMD=checkenv.sh -BASEIMAGE=photon -COMPILETAG=compile_normal -REGISTRYSERVER= -REGISTRYPROJECTNAME=vmware -DEVFLAG=true - -# docker parameters -DOCKERCMD=$(shell which docker) -DOCKERBUILD=$(DOCKERCMD) build -DOCKERRMIMAGE=$(DOCKERCMD) rmi -DOCKERPULL=$(DOCKERCMD) pull -DOCKERIMASES=$(DOCKERCMD) images -DOCKERSAVE=$(DOCKERCMD) save -DOCKERCOMPOSECMD=$(shell which docker-compose) -DOCKERTAG=$(DOCKERCMD) tag - -# go parameters -GOCMD=$(shell which go) -GOBUILD=$(GOCMD) build -GOCLEAN=$(GOCMD) clean -GOINSTALL=$(GOCMD) install -GOTEST=$(GOCMD) test -GODEP=$(GOTEST) -i -GOFMT=gofmt -w -GOBUILDIMAGE=reg.mydomain.com/library/harborgo[:tag] -GOBUILDPATH=$(GOBASEPATH)/harbor -GOIMAGEBUILDCMD=/usr/local/go/bin/go -GOIMAGEBUILD=$(GOIMAGEBUILDCMD) build -GOBUILDPATH_UI=$(GOBUILDPATH)/src/ui -GOBUILDPATH_JOBSERVICE=$(GOBUILDPATH)/src/jobservice -GOBUILDMAKEPATH=$(GOBUILDPATH)/make -GOBUILDMAKEPATH_UI=$(GOBUILDMAKEPATH)/dev/ui -GOBUILDMAKEPATH_JOBSERVICE=$(GOBUILDMAKEPATH)/dev/jobservice -GOLANGDOCKERFILENAME=Dockerfile.golang - -# binary -UISOURCECODE=$(SRCPATH)/ui -UIBINARYPATH=$(MAKEDEVPATH)/ui -UIBINARYNAME=harbor_ui -JOBSERVICESOURCECODE=$(SRCPATH)/jobservice -JOBSERVICEBINARYPATH=$(MAKEDEVPATH)/jobservice -JOBSERVICEBINARYNAME=harbor_jobservice - -# prepare parameters -PREPAREPATH=$(TOOLSPATH) -PREPARECMD=prepare - -# configfile -CONFIGPATH=$(MAKEPATH) -CONFIGFILE=harbor.cfg - -# makefile -MAKEFILEPATH_PHOTON=$(MAKEPATH)/photon -MAKEFILEPATH_UBUNTU=$(MAKEPATH)/ubuntu - -# common dockerfile -DOCKERFILEPATH_COMMON=$(MAKEPATH)/common -DOCKERFILEPATH_DB=$(DOCKERFILEPATH_COMMON)/db -DOCKERFILENAME_DB=Dockerfile - -# docker image name -DOCKERIMAGENAME_UI=vmware/harbor-ui -DOCKERIMAGENAME_JOBSERVICE=vmware/harbor-jobservice -DOCKERIMAGENAME_LOG=vmware/harbor-log -DOCKERIMAGENAME_DB=vmware/harbor-db - - -# docker-compose files -DOCKERCOMPOSEFILEPATH=$(MAKEPATH) -DOCKERCOMPOSETPLFILENAME=docker-compose.tpl -DOCKERCOMPOSEFILENAME=docker-compose.yml - -# version prepare -VERSIONFILEPATH=$(SRCPATH)/ui/views/sections -VERSIONFILENAME=header-content.htm -GITCMD=$(shell which git) -GITTAG=$(GITCMD) describe --tags -ifeq ($(DEVFLAG), true) - VERSIONTAG=dev -else - VERSIONTAG=$(shell $(GITTAG)) -endif - -SEDCMD=$(shell which sed) - -# package -TARCMD=$(shell which tar) -ZIPCMD=$(shell which gzip) -DOCKERIMGFILE=harbor -HARBORPKG=harbor - -# pushimage -PUSHSCRIPTPATH=$(MAKEPATH) -PUSHSCRIPTNAME=pushimage.sh -REGISTRYUSER=user -REGISTRYPASSWORD=default - -version: - @if [ "$(DEVFLAG)" = "false" ] ; then \ - $(SEDCMD) -i 's/version=\"{{.Version}}\"/version=\"$(VERSIONTAG)\"/' -i $(VERSIONFILEPATH)/$(VERSIONFILENAME) ; \ - fi - -check_environment: - @$(MAKEPATH)/$(CHECKENVCMD) - -compile_ui: - @echo "compiling binary for ui..." - @$(GOBUILD) -o $(UIBINARYPATH)/$(UIBINARYNAME) $(UISOURCECODE) - @echo "Done." - -compile_jobservice: - @echo "compiling binary for jobservice..." - @$(GOBUILD) -o $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) $(JOBSERVICESOURCECODE) - @echo "Done." - -compile_normal: compile_ui compile_jobservice - -compile_buildgolangimage: - @echo "compiling golang image for harbor ..." - @$(DOCKERBUILD) -t $(GOBUILDIMAGE) -f $(TOOLSPATH)/$(GOLANGDOCKERFILENAME) . - @echo "Done." - -compile_golangimage: - @echo "compiling binary for ui (golang image)..." - @echo $(GOBASEPATH) - @echo $(GOBUILDPATH) - @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_UI) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_UI)/$(UIBINARYNAME) - @echo "Done." - - @echo "compiling binary for jobservice (golang image)..." - @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_JOBSERVICE) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_JOBSERVICE)/$(JOBSERVICEBINARYNAME) - @echo "Done." - -compile:check_environment $(COMPILETAG) - -prepare: - @echo "preparing..." - @$(MAKEPATH)/$(PREPARECMD) -conf $(CONFIGPATH)/$(CONFIGFILE) - -build_common: version - @echo "buildging db container for photon..." - @cd $(DOCKERFILEPATH_DB) && $(DOCKERBUILD) -f $(DOCKERFILENAME_DB) -t $(DOCKERIMAGENAME_DB):$(VERSIONTAG) . - @echo "Done." - -build_photon: build_common - make -f $(MAKEFILEPATH_PHOTON)/Makefile build -e DEVFLAG=$(DEVFLAG) - -build_ubuntu: build_common - make -f $(MAKEFILEPATH_UBUNTU)/Makefile build -e DEVFLAG=$(DEVFLAG) - -build: build_$(BASEIMAGE) - -modify_composefile: - @echo "preparing docker-compose file..." - @cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) - @$(SEDCMD) -i 's/image\: vmware.*/&:$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) - -install: compile build prepare modify_composefile - @echo "loading harbor images..." - @$(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) up -d - @echo "Install complete. You can visit harbor now." - -package_online: modify_composefile - @echo "packing online package ..." - @cp -r make $(HARBORPKG) - @if [ -n "$(REGISTRYSERVER)" ] ; then \ - $(SEDCMD) -i 's/image\: vmware/image\: $(REGISTRYSERVER)\/$(REGISTRYPROJECTNAME)/' \ - $(HARBORPKG)/docker-compose.yml ; \ - fi - @cp LICENSE $(HARBORPKG)/LICENSE - @cp NOTICE $(HARBORPKG)/NOTICE - @$(TARCMD) -zcvf harbor-online-installer-$(VERSIONTAG).tgz \ - --exclude=$(HARBORPKG)/common/db --exclude=$(HARBORPKG)/common/config\ - --exclude=$(HARBORPKG)/common/log --exclude=$(HARBORPKG)/ubuntu \ - --exclude=$(HARBORPKG)/photon --exclude=$(HARBORPKG)/kubernetes \ - --exclude=$(HARBORPKG)/dev --exclude=$(DOCKERCOMPOSETPLFILENAME) \ - --exclude=$(HARBORPKG)/checkenv.sh \ - --exclude=$(HARBORPKG)/jsminify.sh \ - --exclude=$(HARBORPKG)/pushimage.sh \ - $(HARBORPKG) - - @rm -rf $(HARBORPKG) - @echo "Done." - -package_offline: compile build modify_composefile - @echo "packing offline package ..." - @cp -r make $(HARBORPKG) - - @cp LICENSE $(HARBORPKG)/LICENSE - @cp NOTICE $(HARBORPKG)/NOTICE - - @echo "pulling nginx and registry..." - @$(DOCKERPULL) registry:2.5.0 - @$(DOCKERPULL) nginx:1.11.5 - - @echo "saving harbor docker image" - @$(DOCKERSAVE) -o $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tgz \ - $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ - $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ - nginx:1.11.5 registry:2.5.0 photon:1.0 - - @$(TARCMD) -zcvf harbor-offline-installer-$(VERSIONTAG).tgz \ - --exclude=$(HARBORPKG)/common/db --exclude=$(HARBORPKG)/common/config\ - --exclude=$(HARBORPKG)/common/log --exclude=$(HARBORPKG)/ubuntu \ - --exclude=$(HARBORPKG)/photon --exclude=$(HARBORPKG)/kubernetes \ - --exclude=$(HARBORPKG)/dev --exclude=$(DOCKERCOMPOSETPLFILENAME) \ - --exclude=$(HARBORPKG)/checkenv.sh \ - --exclude=$(HARBORPKG)/jsminify.sh \ - --exclude=$(HARBORPKG)/pushimage.sh \ - $(HARBORPKG) - - @rm -rf $(HARBORPKG) - @echo "Done." - -pushimage: - @echo "pushing harbor images ..." - @$(DOCKERTAG) $(DOCKERIMAGENAME_UI):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) - - @$(DOCKERTAG) $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) - - @$(DOCKERTAG) $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) - - @$(DOCKERTAG) $(DOCKERIMAGENAME_DB):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) - @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ - $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) - @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) - -start: - @echo "loading harbor images..." - @$(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml up -d - @echo "Start complete. You can visit harbor now." - -down: - @echo "stoping harbor instance..." - @$(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml down - @echo "Done." - -cleanbinary: - @echo "cleaning binary..." - @if [ -f $(UIBINARYPATH)/$(UIBINARYNAME) ] ; then rm $(UIBINARYPATH)/$(UIBINARYNAME) ; fi - @if [ -f $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ] ; then rm $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ; fi - -cleanimage: - @echo "cleaning image for photon..." - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_UI):$(VERSIONTAG) - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_DB):$(VERSIONTAG) - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) - - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) -# - $(DOCKERRMIMAGE) -f registry:2.5.0 -# - $(DOCKERRMIMAGE) -f nginx:1.11.5 - -cleandockercomposefile: - @echo "cleaning $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml" - @if [ -f $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ] ; then rm $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ; fi - -cleanversiontag: - @echo "cleaning version TAG" - @$(SEDCMD) -i 's/version=\"$(VERSIONTAG)\"/version=\"{{.Version}}\"/' -i $(VERSIONFILEPATH)/$(VERSIONFILENAME) - -cleanpackage: - @echo "cleaning harbor install package" - @if [ -d $(BUILDPATH)/harbor ] ; then rm -rf $(BUILDPATH)/harbor ; fi - @if [ -f $(BUILDPATH)/harbor-online-installer-$(VERSIONTAG).tgz ] ; \ - then rm $(BUILDPATH)/harbor-online-installer-$(VERSIONTAG).tgz ; fi - @if [ -f $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ] ; \ - then rm $(BUILDPATH)/harbor-offline-installer-$(VERSIONTAG).tgz ; fi - -.PHONY: cleanall -cleanall: cleanbinary cleanimage cleandockercomposefile cleanversiontag cleanpackage - -clean: - @echo " make cleanall: remove binary, Harbor images, specific version docker-compose" - @echo " file, specific version tag, online and offline install package" - @echo " make cleanbinary: remove ui and jobservice binary" - @echo " make cleanimage: remove Harbor images" - @echo " make cleandockercomposefile: remove specific version docker-compose" - @echo " make cleanversiontag: cleanpackageremove specific version tag" - @echo " make cleanpackage: remove online and offline install package" - -all: install +# Makefile for Harbor project +# +# Targets: +# +# all: prepare env, compile binarys, build images and install images +# prepare: prepare env +# compile: compile adminserver, ui and jobservice code +# +# compile_golangimage: +# compile from golang image +# for example: make compile_golangimage -e GOBUILDIMAGE= \ +# golang:1.7.3 +# compile_adminserver, compile_ui, compile_jobservice: compile specific binary +# +# build: build Harbor docker images (defuault: build_photon) +# for example: make build -e BASEIMAGE=photon +# build_photon: build Harbor docker images from photon baseimage +# +# install: include compile binarys, build images, prepare specific \ +# version composefile and startup Harbor instance +# +# start: startup Harbor instance +# +# down: shutdown Harbor instance +# +# package_online: +# prepare online install package +# for example: make package_online -e DEVFLAG=false\ +# REGISTRYSERVER=reg-bj.eng.vmware.com \ +# REGISTRYPROJECTNAME=harborrelease +# +# package_offline: +# prepare offline install package +# +# pushimage: push Harbor images to specific registry server +# for example: make pushimage -e DEVFLAG=false REGISTRYUSER=admin \ +# REGISTRYPASSWORD=***** \ +# REGISTRYSERVER=reg-bj.eng.vmware.com/ \ +# REGISTRYPROJECTNAME=harborrelease +# note**: need add "/" on end of REGISTRYSERVER. If not setting \ +# this value will push images directly to dockerhub. +# make pushimage -e DEVFLAG=false REGISTRYUSER=vmware \ +# REGISTRYPASSWORD=***** \ +# REGISTRYPROJECTNAME=vmware +# +# clean: remove binary, Harbor images, specific version docker-compose \ +# file, specific version tag and online/offline install package +# cleanbinary: remove adminserver, ui and jobservice binary +# cleanimage: remove Harbor images +# cleandockercomposefile: +# remove specific version docker-compose +# cleanversiontag: +# cleanpackageremove specific version tag +# cleanpackage: remove online/offline install package +# +# other example: +# clean specific version binarys and images: +# make clean -e VERSIONTAG=[TAG] +# note**: If commit new code to github, the git commit TAG will \ +# change. Better use this commond clean previous images and \ +# files with specific TAG. +# By default DEVFLAG=true, if you want to release new version of Harbor, \ +# should setting the flag to false. +# make XXXX -e DEVFLAG=false + +SHELL := /bin/bash +BUILDPATH=$(CURDIR) +MAKEPATH=$(BUILDPATH)/make +MAKEDEVPATH=$(MAKEPATH)/dev +SRCPATH=./src +TOOLSPATH=$(BUILDPATH)/tools +UIPATH=$(BUILDPATH)/src/ui +UINGPATH=$(BUILDPATH)/src/ui_ng +GOBASEPATH=/go/src/github.com/vmware +CHECKENVCMD=checkenv.sh +BASEIMAGE=photon +COMPILETAG=compile_normal +REGISTRYSERVER= +REGISTRYPROJECTNAME=vmware +DEVFLAG=true +NOTARYFLAG=false +REGISTRYVERSION=photon-2.6.0 +NGINXVERSION=1.11.5-patched +PHOTONVERSION=1.0 +NOTARYVERSION=server-0.5.0 +NOTARYSIGNERVERSION=signer-0.5.0 +MARIADBVERSION=mariadb-10.1.10 +HTTPPROXY= + +#clarity parameters +CLARITYIMAGE=vmware/harbor-clarity-ui-builder[:tag] +CLARITYSEEDPATH=/clarity-seed +CLARITYBUILDSCRIPT=/entrypoint.sh + +# docker parameters +DOCKERCMD=$(shell which docker) +DOCKERBUILD=$(DOCKERCMD) build +DOCKERRMIMAGE=$(DOCKERCMD) rmi +DOCKERPULL=$(DOCKERCMD) pull +DOCKERIMASES=$(DOCKERCMD) images +DOCKERSAVE=$(DOCKERCMD) save +DOCKERCOMPOSECMD=$(shell which docker-compose) +DOCKERTAG=$(DOCKERCMD) tag + +# go parameters +GOCMD=$(shell which go) +GOBUILD=$(GOCMD) build +GOCLEAN=$(GOCMD) clean +GOINSTALL=$(GOCMD) install +GOTEST=$(GOCMD) test +GODEP=$(GOTEST) -i +GOFMT=gofmt -w +GOBUILDIMAGE=reg.mydomain.com/library/harborgo[:tag] +GOBUILDPATH=$(GOBASEPATH)/harbor +GOIMAGEBUILDCMD=/usr/local/go/bin/go +GOIMAGEBUILD=$(GOIMAGEBUILDCMD) build +GOBUILDPATH_ADMINSERVER=$(GOBUILDPATH)/src/adminserver +GOBUILDPATH_UI=$(GOBUILDPATH)/src/ui +GOBUILDPATH_JOBSERVICE=$(GOBUILDPATH)/src/jobservice +GOBUILDMAKEPATH=$(GOBUILDPATH)/make +GOBUILDMAKEPATH_ADMINSERVER=$(GOBUILDMAKEPATH)/dev/adminserver +GOBUILDMAKEPATH_UI=$(GOBUILDMAKEPATH)/dev/ui +GOBUILDMAKEPATH_JOBSERVICE=$(GOBUILDMAKEPATH)/dev/jobservice +GOLANGDOCKERFILENAME=Dockerfile.golang + +# binary +ADMINSERVERSOURCECODE=$(SRCPATH)/adminserver +ADMINSERVERBINARYPATH=$(MAKEDEVPATH)/adminserver +ADMINSERVERBINARYNAME=harbor_adminserver +UISOURCECODE=$(SRCPATH)/ui +UIBINARYPATH=$(MAKEDEVPATH)/ui +UIBINARYNAME=harbor_ui +JOBSERVICESOURCECODE=$(SRCPATH)/jobservice +JOBSERVICEBINARYPATH=$(MAKEDEVPATH)/jobservice +JOBSERVICEBINARYNAME=harbor_jobservice + +# prepare parameters +PREPAREPATH=$(TOOLSPATH) +PREPARECMD=prepare + +# configfile +CONFIGPATH=$(MAKEPATH) +CONFIGFILE=harbor.cfg + +# makefile +MAKEFILEPATH_PHOTON=$(MAKEPATH)/photon + +# common dockerfile +DOCKERFILEPATH_COMMON=$(MAKEPATH)/common +DOCKERFILEPATH_DB=$(DOCKERFILEPATH_COMMON)/db +DOCKERFILENAME_DB=Dockerfile + +# docker image name +DOCKERIMAGENAME_ADMINSERVER=vmware/harbor-adminserver +DOCKERIMAGENAME_UI=vmware/harbor-ui +DOCKERIMAGENAME_JOBSERVICE=vmware/harbor-jobservice +DOCKERIMAGENAME_LOG=vmware/harbor-log +DOCKERIMAGENAME_DB=vmware/harbor-db + +# docker-compose files +DOCKERCOMPOSEFILEPATH=$(MAKEPATH) +DOCKERCOMPOSETPLFILENAME=docker-compose.tpl +DOCKERCOMPOSEFILENAME=docker-compose.yml +DOCKERCOMPOSENOTARYFILENAME=docker-compose.notary.yml + +# version prepare +VERSIONFILEPATH=$(CURDIR) +VERSIONFILENAME=VERSION +GITCMD=$(shell which git) +GITTAG=$(GITCMD) describe --tags +GITTAGVERSION=$(shell git describe --tags || echo UNKNOWN) +ifeq ($(DEVFLAG), true) + VERSIONTAG=dev +else + VERSIONTAG=$(GITTAGVERSION) +endif + +SEDCMD=$(shell which sed) + +# package +TARCMD=$(shell which tar) +ZIPCMD=$(shell which gzip) +DOCKERIMGFILE=harbor +HARBORPKG=harbor + +# pushimage +PUSHSCRIPTPATH=$(MAKEPATH) +PUSHSCRIPTNAME=pushimage.sh +REGISTRYUSER=user +REGISTRYPASSWORD=default + +version: + @printf $(GITTAGVERSION) > $(VERSIONFILEPATH)/$(VERSIONFILENAME); + +check_environment: + @$(MAKEPATH)/$(CHECKENVCMD) + +compile_adminserver: + @echo "compiling binary for adminserver..." + @$(GOBUILD) -o $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) $(ADMINSERVERSOURCECODE) + @echo "Done." + +compile_ui: + @echo "compiling binary for ui..." + @$(GOBUILD) -o $(UIBINARYPATH)/$(UIBINARYNAME) $(UISOURCECODE) + @echo "Done." + +compile_jobservice: + @echo "compiling binary for jobservice..." + @$(GOBUILD) -o $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) $(JOBSERVICESOURCECODE) + @echo "Done." + +compile_clarity: + @echo "compiling binary for clarity ui..." + @if [ "$(HTTPPROXY)" != "" ] ; then \ + $(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT) -p $(HTTPPROXY); \ + else \ + $(DOCKERCMD) run --rm -v $(UIPATH)/static:$(CLARITYSEEDPATH)/dist -v $(UINGPATH)/src:$(CLARITYSEEDPATH)/src $(CLARITYIMAGE) $(SHELL) $(CLARITYBUILDSCRIPT); \ + fi + @echo "Done." + +compile_normal: compile_clarity compile_adminserver compile_ui compile_jobservice + +compile_golangimage: compile_clarity + @echo "compiling binary for adminserver (golang image)..." + @echo $(GOBASEPATH) + @echo $(GOBUILDPATH) + @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_ADMINSERVER) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_ADMINSERVER)/$(ADMINSERVERBINARYNAME) + @echo "Done." + + @echo "compiling binary for ui (golang image)..." + @echo $(GOBASEPATH) + @echo $(GOBUILDPATH) + @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_UI) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_UI)/$(UIBINARYNAME) + @echo "Done." + + @echo "compiling binary for jobservice (golang image)..." + @$(DOCKERCMD) run --rm -v $(BUILDPATH):$(GOBUILDPATH) -w $(GOBUILDPATH_JOBSERVICE) $(GOBUILDIMAGE) $(GOIMAGEBUILD) -v -o $(GOBUILDMAKEPATH_JOBSERVICE)/$(JOBSERVICEBINARYNAME) + @echo "Done." + +compile:check_environment $(COMPILETAG) + +prepare: + @echo "preparing..." + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(MAKEPATH)/$(PREPARECMD) --conf $(CONFIGPATH)/$(CONFIGFILE) --with-notary; \ + else \ + $(MAKEPATH)/$(PREPARECMD) --conf $(CONFIGPATH)/$(CONFIGFILE) ; \ + fi + +build_common: version + @echo "buildging db container for photon..." + @cd $(DOCKERFILEPATH_DB) && $(DOCKERBUILD) -f $(DOCKERFILENAME_DB) -t $(DOCKERIMAGENAME_DB):$(VERSIONTAG) . + @echo "Done." + +build_photon: build_common + make -f $(MAKEFILEPATH_PHOTON)/Makefile build -e DEVFLAG=$(DEVFLAG) + +build: build_$(BASEIMAGE) + +modify_composefile: + @echo "preparing docker-compose file..." + @cp $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSETPLFILENAME) $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) + @$(SEDCMD) -i 's/__version__/$(VERSIONTAG)/g' $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) + +modify_sourcefiles: + @echo "change mode of source files." + @chmod 600 $(MAKEPATH)/common/templates/notary/notary-signer.key + @chmod 600 $(MAKEPATH)/common/templates/notary/notary-signer.crt + @chmod 600 $(MAKEPATH)/common/templates/notary/notary-signer-ca.crt + @chmod 600 $(MAKEPATH)/common/templates/ui/private_key.pem + @chmod 600 $(MAKEPATH)/common/templates/registry/root.crt + +install: compile build modify_sourcefiles prepare modify_composefile start + +package_online: modify_composefile + @echo "packing online package ..." + @cp -r make $(HARBORPKG) + @if [ -n "$(REGISTRYSERVER)" ] ; then \ + $(SEDCMD) -i 's/image\: vmware/image\: $(REGISTRYSERVER)\/$(REGISTRYPROJECTNAME)/' \ + $(HARBORPKG)/docker-compose.yml ; \ + fi + @cp LICENSE $(HARBORPKG)/LICENSE + @cp NOTICE $(HARBORPKG)/NOTICE + + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(TARCMD) -zcvf harbor-online-installer-$(GITTAGVERSION).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/prepare \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/install.sh $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ + $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME); \ + else \ + $(TARCMD) -zcvf harbor-online-installer-$(GITTAGVERSION).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/prepare \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/install.sh $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ + $(HARBORPKG)/harbor.cfg ; \ + fi + + @rm -rf $(HARBORPKG) + @echo "Done." + +package_offline: compile build modify_sourcefiles modify_composefile + @echo "packing offline package ..." + @cp -r make $(HARBORPKG) + + @cp LICENSE $(HARBORPKG)/LICENSE + @cp NOTICE $(HARBORPKG)/NOTICE + + @echo "pulling nginx and registry..." + @$(DOCKERPULL) vmware/registry:$(REGISTRYVERSION) + @$(DOCKERPULL) vmware/nginx:$(NGINXVERSION) + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + echo "pulling notary and harbor-notary-db..."; \ + $(DOCKERPULL) vmware/notary-photon:$(NOTARYVERSION); \ + $(DOCKERPULL) vmware/notary-photon:$(NOTARYSIGNERVERSION); \ + $(DOCKERPULL) vmware/harbor-notary-db:$(MARIADBVERSION); \ + fi + + @echo "saving harbor docker image" + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(DOCKERSAVE) $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ + vmware/nginx:$(NGINXVERSION) vmware/registry:$(REGISTRYVERSION) photon:$(PHOTONVERSION) \ + vmware/notary-photon:$(NOTARYVERSION) vmware/notary-photon:$(NOTARYSIGNERVERSION) \ + vmware/harbor-notary-db:$(MARIADBVERSION) | gzip > $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tar.gz; \ + else \ + $(DOCKERSAVE) $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ + $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ + vmware/nginx:$(NGINXVERSION) vmware/registry:$(REGISTRYVERSION) \ + photon:$(PHOTONVERSION) | gzip > $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tar.gz; \ + fi + + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(TARCMD) -zcvf harbor-offline-installer-$(GITTAGVERSION).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tar.gz \ + $(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \ + $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) \ + $(HARBORPKG)/$(DOCKERCOMPOSENOTARYFILENAME) ; \ + else \ + $(TARCMD) -zcvf harbor-offline-installer-$(GITTAGVERSION).tgz \ + $(HARBORPKG)/common/templates $(HARBORPKG)/$(DOCKERIMGFILE).$(VERSIONTAG).tar.gz \ + $(HARBORPKG)/prepare $(HARBORPKG)/NOTICE \ + $(HARBORPKG)/LICENSE $(HARBORPKG)/install.sh \ + $(HARBORPKG)/harbor.cfg $(HARBORPKG)/$(DOCKERCOMPOSEFILENAME) ; \ + fi + + @rm -rf $(HARBORPKG) + @echo "Done." + +pushimage: + @echo "pushing harbor images ..." + @$(DOCKERTAG) $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) + + @$(DOCKERTAG) $(DOCKERIMAGENAME_UI):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_UI):$(VERSIONTAG) + + @$(DOCKERTAG) $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) + + @$(DOCKERTAG) $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_LOG):$(VERSIONTAG) + + @$(DOCKERTAG) $(DOCKERIMAGENAME_DB):$(VERSIONTAG) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) + @$(PUSHSCRIPTPATH)/$(PUSHSCRIPTNAME) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) \ + $(REGISTRYUSER) $(REGISTRYPASSWORD) $(REGISTRYSERVER) + @$(DOCKERRMIMAGE) $(REGISTRYSERVER)$(DOCKERIMAGENAME_DB):$(VERSIONTAG) + +start: + @echo "loading harbor images..." + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) up -d ; \ + else \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) up -d ; \ + fi + @echo "Start complete. You can visit harbor now." + +down: + @echo "Please make sure to set -e NOTARYFLAG=true if you are using Notary in Harbor, otherwise the Notary containers cannot be stop automaticlly." + @while [ -z "$$CONTINUE" ]; do \ + read -r -p "Type anything but Y or y to exit. [Y/N]: " CONTINUE; \ + done ; \ + [ $$CONTINUE = "y" ] || [ $$CONTINUE = "Y" ] || (echo "Exiting."; exit 1;) + @echo "stoping harbor instance..." + @if [ "$(NOTARYFLAG)" = "true" ] ; then \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSENOTARYFILENAME) down -v ; \ + else \ + $(DOCKERCOMPOSECMD) -f $(DOCKERCOMPOSEFILEPATH)/$(DOCKERCOMPOSEFILENAME) down -v ; \ + fi + @echo "Done." + +cleanbinary: + @echo "cleaning binary..." + @if [ -f $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ] ; then rm $(ADMINSERVERBINARYPATH)/$(ADMINSERVERBINARYNAME) ; fi + @if [ -f $(UIBINARYPATH)/$(UIBINARYNAME) ] ; then rm $(UIBINARYPATH)/$(UIBINARYNAME) ; fi + @if [ -f $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ] ; then rm $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ; fi + +cleanimage: + @echo "cleaning image for photon..." + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_ADMINSERVER):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_UI):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_DB):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_JOBSERVICE):$(VERSIONTAG) + - $(DOCKERRMIMAGE) -f $(DOCKERIMAGENAME_LOG):$(VERSIONTAG) +# - $(DOCKERRMIMAGE) -f registry:$(REGISTRYVERSION) +# - $(DOCKERRMIMAGE) -f nginx:1.11.5 + +cleandockercomposefile: + @echo "cleaning $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml" + @if [ -f $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ] ; then rm $(DOCKERCOMPOSEFILEPATH)/docker-compose.yml ; fi + +cleanversiontag: + @echo "cleaning version TAG" + @rm -rf $(VERSIONFILEPATH)/$(VERSIONFILENAME) + +cleanpackage: + @echo "cleaning harbor install package" + @if [ -d $(BUILDPATH)/harbor ] ; then rm -rf $(BUILDPATH)/harbor ; fi + @if [ -f $(BUILDPATH)/harbor-online-installer-$(GITTAGVERSION).tgz ] ; \ + then rm $(BUILDPATH)/harbor-online-installer-$(GITTAGVERSION).tgz ; fi + @if [ -f $(BUILDPATH)/harbor-offline-installer-$(GITTAGVERSION).tgz ] ; \ + then rm $(BUILDPATH)/harbor-offline-installer-$(GITTAGVERSION).tgz ; fi + +.PHONY: cleanall +cleanall: cleanbinary cleanimage cleandockercomposefile cleanversiontag cleanpackage + +clean: + @echo " make cleanall: remove binary, Harbor images, specific version docker-compose" + @echo " file, specific version tag, online and offline install package" + @echo " make cleanbinary: remove ui and jobservice binary" + @echo " make cleanimage: remove Harbor images" + @echo " make cleandockercomposefile: remove specific version docker-compose" + @echo " make cleanversiontag: cleanpackageremove specific version tag" + @echo " make cleanpackage: remove online and offline install package" + +all: install diff --git a/NOTICE b/NOTICE index dad670b59..fd1e6a1fb 100644 --- a/NOTICE +++ b/NOTICE @@ -1,8 +1,8 @@ NOTICE -Harbor version 0.5.0 GA +Harbor 1.1.0 -Copyright (c) 2016 VMware, Inc. All Rights Reserved. +Copyright (c) 2017 VMware, Inc. All Rights Reserved. This product is licensed to you under the Apache License, Version 2.0 (the "License"). You may not use this product except in compliance with the License. diff --git a/README.md b/README.md index f70ffd44e..a1d4eccbd 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ [![Build Status](https://travis-ci.org/vmware/harbor.svg?branch=master)](https://travis-ci.org/vmware/harbor) [![Coverage Status](https://coveralls.io/repos/github/vmware/harbor/badge.svg?branch=dev)](https://coveralls.io/github/vmware/harbor?branch=dev) +**Note**: The `master` branch may be in an *unstable or even broken state* during development. +Please use [releases] instead of the `master` branch in order to get stable binaries. + Harbor Project Harbor is an enterprise-class registry server that stores and distributes Docker images. Harbor extends the open source Docker Distribution by adding the functionalities usually required by an enterprise, such as security, identity and management. As an enterprise private registry, Harbor offers better performance and security. Having a registry closer to the build and run environment improves the image transfer efficiency. Harbor supports the setup of multiple registries and has images replicated between them. In addition, Harbor offers advanced security features, such as user management, access control and activity auditing. @@ -12,10 +15,11 @@ Project Harbor is an enterprise-class registry server that stores and distribute * **Policy based image replication**: Images can be replicated (synchronized) between multiple registry instances. Great for load balancing, high availability, multi-datacenter, hybrid and multi-cloud scenarios. * **LDAP/AD support**: Harbor integrates with existing enterprise LDAP/AD for user authentication and management. * **Image deletion & garbage collection**: Images can be deleted and their space can be recycled. +* **Notary**: Image authenticity can be ensured. * **Graphical user portal**: User can easily browse, search repositories and manage projects. * **Auditing**: All the operations to the repositories are tracked. * **RESTful API**: RESTful APIs for most administrative operations, easy to integrate with external systems. -* **Easy deployment**: Provide both an online and offline installer. Besides, a virtual appliance for vSphere platform (OVA) is available. +* **Easy deployment**: Provide both an online and offline installer. ### Install & Run @@ -23,8 +27,6 @@ Project Harbor is an enterprise-class registry server that stores and distribute **On a Linux host:** docker 1.10.0+ and docker-compose 1.6.0+ . -**On vSphere:** vCenter 5.5+ for deployment of Harbor's virtual appliance. - Download binaries of **[Harbor release ](https://github.com/vmware/harbor/releases)** and follow **[Installation & Configuration Guide](docs/installation_guide.md)** to install Harbor. Refer to **[User Guide](docs/user_guide.md)** for more details on how to use Harbor. @@ -44,6 +46,6 @@ Harbor is available under the [Apache 2 license](LICENSE). This project uses open source components which have additional licensing terms. The official docker images and licensing terms for these open source components can be found at the following locations: * Photon OS 1.0: [docker image](https://hub.docker.com/_/photon/), [license](https://github.com/vmware/photon/blob/master/COPYING) -* Docker Registry 2.5: [docker image](https://hub.docker.com/_/registry/), [license](https://github.com/docker/distribution/blob/master/LICENSE) +* Docker Registry 2.6: [docker image](https://hub.docker.com/_/registry/), [license](https://github.com/docker/distribution/blob/master/LICENSE) * MySQL 5.6: [docker image](https://hub.docker.com/_/mysql/), [license](https://github.com/docker-library/mysql/blob/master/LICENSE) * NGINX 1.11.5: [docker image](https://hub.docker.com/_/nginx/), [license](https://github.com/nginxinc/docker-nginx/blob/master/LICENSE) diff --git a/ROADMAP.md b/ROADMAP.md index 72cea6784..18738072a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -20,28 +20,22 @@ The notary feature allows publishers to sign their images offline and to push th ### 2. Vulnerability Scanning The capability to scan images for vulnerability. -### 3. Image replication between Harbor instances (Completed) -Enable images to be replicated between two or more Harbor instances. This is useful to have multiple registry servers servicing a large cluster of nodes, or have distributed registry instances with identical images. +### 3. Image replication enhancement +To provide more sophisticated rule for image replication. +- Image filtering by tags +- Replication can be scheduled at a certain time using a rule like: one time only, daily, weekly, etc. +- Image deletion can have the option not to be replicated to a remote instance. +- Global replication rule: Instead of setting the rule of individual project, system admin can set a global rule for all projects. +- Project admin can set replication policy of the project. -### 4. Image deletion and garbage collection (Completed) -a) Images can be deleted from UI. The files of deleted images are not removed immediately. - -b) The files of deleted images are recycled by an administrator during system maintenance(Garbage collection). The registry service must be shut down during the process of garbage collection. - - -### 5. Authentication (OAuth2) +### 4. Authentication (OAuth2) In addition to LDAP/AD and local users, OAuth 2.0 can be used to authenticate a user. -### 6. High Availability (in progress) +### 5. High Availability Support multi-node deployment of Harbor for high availability, scalability and load-balancing purposes. -### 7. Statistics and description for repositories +### 6. Statistics and description for repositories User can add a description to a repository. The access count of a repo can be aggregated and displayed. - -### 8. Audit all operations in the system -Currently only image related operations are logged. Other operations in Harbor, such as user creation/deletion, role changes, password reset, should be tracked as well. - - -### 9. Migration tool to move from an existing registry to Harbor +### 7. Migration tool to move from an existing registry to Harbor A tool to migrate images from a vanilla registry server to Harbor, without the need to export/import a large amount of data. diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..90012116c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +dev \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 555a778b5..24c35183e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,9 +6,6 @@ Read this first!** Guide for Harbor online installer and offline installer. -**[Installation and Configuration Guide for Virtual Appliance](installation_guide_ova.md)** -Guide for installing Harbor on vSphere, either standalone or as part of vSphere Integrated Containers (VIC). - **[Harbor User Guide](user_guide.md)** How to use Harbor to manage images, projects, replications and users. @@ -57,11 +54,18 @@ How to add your local language to Harbor. [Working with Harbor Registry REST API via Swagger](http://www.think-foundry.com/working-with-harbor-registry-rest-api-via-swagger/) +[How to use Harbor with Minio](https://blog.minio.io/how-to-use-vmware-harbor-with-minio-c07a5c4ae31b) + +[Harbor, an enterprise class registry server](https://vorcunus.blog/2017/03/11/harbor-an-enterprise-class-registry-server/) + +[Hybrid Container Management for vCloud Director with Harbor](https://blogs.vmware.com/vcat/2017/03/hybrid-container-management-vcloud-director-vmware-harbor.html) + +[Project Harbor Reached Milestone of 2000 Stars](http://www.think-foundry.com/project-harbor-reaches-milestone-2000-stars-github/) + [Project Harbor in action](http://cormachogan.com/2016/08/05/project-harbor-action/) [Using vSphere docker volume driver to run Project Harbor on VSAN](http://cormachogan.com/2016/07/29/using-vsphere-docker-volume-driver-run-project-harbor-vsan/) - [Overall Architecture of Harbor Registry](http://www.compare-review-information.com/overall-architecture-of-harbor-registry/) [Making a Private Secured Docker Registry in 15 Minutes](http://alexanderzeitler.com/articles/deploying-a-private-secured-docker-registry-within-15-minutes/) diff --git a/docs/compile_guide.md b/docs/compile_guide.md index 0b1c8f4e9..b0dac6a4c 100644 --- a/docs/compile_guide.md +++ b/docs/compile_guide.md @@ -1,21 +1,21 @@ ## Introduction -This guide provides instructions for developers to build and run Harbor from source code. +This guide provides instructions for developers to build and run Harbor from source code. ## Step 1: Prepare for a build environment for Harbor -Harbor is deployed as several Docker containers and most of the code is written in Go language. The build host requires Python, Docker, Docker Compose and golang development environment. Please install the below prerequisites: +Harbor is deployed as several Docker containers and most of the code is written in Go language. The build environment requires Python, Docker, Docker Compose and golang development environment. Please install the below prerequisites: Software | Required Version ----------------------|-------------------------- -docker | 1.10.0 + -docker-compose | 1.7.1 + +docker | 1.12.0 + +docker-compose | 1.11.0 + python | 2.7 + git | 1.9.1 + make | 3.81 + -golang* | 1.6.0 + - *optional +golang* | 1.7.3 + +*optional, required only if you use your own Golang environment. ## Step 2: Getting the source code @@ -24,52 +24,39 @@ golang* | 1.6.0 + $ git clone https://github.com/vmware/harbor ``` -## Step 3: Resolving dependencies of Go language -You can compile the source code by using a Golang dev image. In this case, you can skip this step. - -If you are building Harbor using your own Go compiling environment. You need to install LDAP packages manually. - -For PhotonOS: - - ```sh - $ tdnf install -y sed apr-util-ldap - ``` - -For Ubuntu: - - ```sh - $ apt-get update && apt-get install -y libldap2-dev - ``` - -For other platforms, please consult the relevant documentation of installing LDAP package. - -## Step 4: Building and installing Harbor +## Step 3: Building and installing Harbor ### Configuration -Edit the file **make/harbor.cfg** and make necessary configuration changes such as hostname, admin password and mail server. Refer to **[Installation and Configuration Guide](installation_guide.md#configuring-harbor)** for more info. +Edit the file **make/harbor.cfg** and make necessary configuration changes such as hostname, admin password and mail server. Refer to **[Installation and Configuration Guide](installation_guide.md#configuring-harbor)** for more info. ```sh $ cd harbor $ vi make/harbor.cfg ``` - + ### Compiling and Running You can compile the code by one of the three approaches: -#### I. Create a Golang dev image, then build Harbor +#### I. Build with offical Golang image -* Build Golang dev image: +* Get offcial Golang image from docker hub: ```sh - $ make compile_buildgolangimage -e GOBUILDIMAGE=harborgo:1.6.2 + $ docker pull golang:1.7.3 ``` -* Build, install and bring up Harbor: +* Build, install and bring up Harbor without Notary: ```sh - $ make install -e GOBUILDIMAGE=harborgo:1.6.2 COMPILETAG=compile_golangimage + $ make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:0.8.4 + ``` + +* Build, install and bring up Harbor with Notary: + + ```sh + $ make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:0.8.4 NOTARYFLAG=true ``` #### II. Compile code with your own Golang environment, then build Harbor @@ -82,40 +69,27 @@ You can compile the code by one of the three approaches: $ mv harbor $GOPATH/src/github.com/vmware/. ``` -* Build, install and run Harbor +* Build, install and run Harbor without Notary: ```sh $ cd $GOPATH/src/github.com/vmware/harbor $ make install ``` - -#### III. Manual build process (compatible with previous versions) + +* Build, install and run Harbor with Notary: ```sh - $ cd make - - $ ./prepare - Generated configuration file: ./config/ui/env - Generated configuration file: ./config/ui/app.conf - Generated configuration file: ./config/registry/config.yml - Generated configuration file: ./config/db/env - ... - - $ cd dev - - $ docker-compose up -d - ``` + $ cd $GOPATH/src/github.com/vmware/harbor + $ make install -e NOTARYFLAG=true + ``` ### Verify your installation -If everyting worked properly, you can get the below message: +If everything worked properly, you can get the below message: ```sh ... - ----Harbor has been installed and started successfully.---- - - Now you should be able to visit the admin portal at http://$YOURIP. - For more details, please visit https://github.com/vmware/harbor . + Start complete. You can visit harbor now. ``` Refer to [Installation and Configuration Guide](installation_guide.md#managing-harbors-lifecycle) for more information about managing your Harbor instance. @@ -128,8 +102,11 @@ The `Makefile` contains these configurable parameters: Variable | Description -------------------|------------- BASEIMAGE | Container base image, default: photon +CLARITYIMAGE | Clarity UI builder image, default: harbor-clarity-ui-builder DEVFLAG | Build model flag, default: dev COMPILETAG | Compile model flag, default: compile_normal (local golang build) +NOTARYFLAG | Notary mode flag, default: false +HTTPPROXY | NPM http proxy for Clarity UI builder REGISTRYSERVER | Remote registry server IP address REGISTRYUSER | Remote registry server user name REGISTRYPASSWORD | Remote registry server user password @@ -139,44 +116,29 @@ REGISTRYPROJECTNAME| Project name on remote registry server Target | Description --------------------|------------- -all | prepare env, compile binaries, build images and install images +all | prepare env, compile binaries, build images and install images prepare | prepare env compile | compile ui and jobservice code -compile_golangimage | compile local golang image compile_ui | compile ui binary compile_jobservice | compile jobservice binary +compile_clarity | compile Clarity binary build | build Harbor docker images (default: using build_photon) build_photon | build Harbor docker images from Photon OS base image -build_ubuntu | build Harbor docker images from Ubuntu base image install | compile binaries, build images, prepare specific version of compose file and startup Harbor instance -start | startup Harbor instance -down | shutdown Harbor instance +start | startup Harbor instance (set NOTARYFLAG=true when with Notary) +down | shutdown Harbor instance (set NOTARYFLAG=true when with Notary) package_online | prepare online install package package_offline | prepare offline install package pushimage | push Harbor images to specific registry server clean all | remove binary, Harbor images, specific version docker-compose file, specific version tag and online/offline install package cleanbinary | remove ui and jobservice binary -cleanimage | remove Harbor images -cleandockercomposefile | remove specific version docker-compose +cleanimage | remove Harbor images +cleandockercomposefile | remove specific version docker-compose cleanversiontag | remove specific version tag cleanpackage | remove online/offline install package #### EXAMPLE: -#### Build a golang dev image (for building Harbor): - - ```sh - $ make compile_golangimage -e GOBUILDIMAGE= [$YOURIMAGE] - - ``` - -#### Build Harbor images based on Ubuntu - - ```sh - $ make build -e BASEIMAGE=ubuntu - - ``` - #### Push Harbor images to specific registry server ```sh @@ -198,7 +160,7 @@ cleanpackage | remove online/offline install package $ make clean -e VERSIONTAG=[TAG] ``` - **Note**: If new code had been added to Github, the git commit TAG will change. Better use this command to clean up images and files of previous TAG. + **Note**: If new code had been added to Github, the git commit TAG will change. Better use this command to clean up images and files of previous TAG. #### By default, the make process create a development build. To create a release build of Harbor, set the below flag to false. @@ -206,4 +168,3 @@ cleanpackage | remove online/offline install package $ make XXXX -e DEVFLAG=false ``` - diff --git a/docs/configure_https.md b/docs/configure_https.md index 0c3641066..f1b9b843d 100644 --- a/docs/configure_https.md +++ b/docs/configure_https.md @@ -1,8 +1,8 @@ -#Configuring Harbor with HTTPS Access +# Configuring Harbor with HTTPS Access Because Harbor does not ship with any certificates, it uses HTTP by default to serve registry requests. However, it is highly recommended that security be enabled for any production environment. Harbor has an Nginx instance as a reverse proxy for all services, you can use the prepare script to configure Nginx to enable https. -##Getting a certificate +## Getting a certificate Assuming that your registry's **hostname** is **reg.yourdomain.com**, and that its DNS record points to the host where you are running Harbor. You first should get a certificate from a CA. The certificate usually contains a .crt file and a .key file, for example, **yourdomain.com.crt** and **yourdomain.com.key**. @@ -40,7 +40,7 @@ If you're using **IP**, say **192.168.1.101** to connect your registry host, you openssl x509 -req -days 365 -in yourdomain.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -extfile extfile.cnf -out yourdomain.com .crt ``` -##Configuration and Installation +## Configuration and Installation After obtaining the **yourdomain.com.crt** and **yourdomain.com.key** files, you can put them into directory such as ```/root/cert/```: @@ -95,7 +95,7 @@ If you've mapped nginx 443 port to another, you need to add the port to login, l docker login reg.yourdomain.com:port ``` -##Troubleshooting +## Troubleshooting 1. You may get an intermediate certificate from a certificate issuer. In this case, you should merge the intermediate certificate with your own certificate to create a certificate bundle. You can achieve this by the below command: ``` diff --git a/docs/customize_token_service.md b/docs/customize_token_service.md index 93694a1f8..e7344b26a 100644 --- a/docs/customize_token_service.md +++ b/docs/customize_token_service.md @@ -1,4 +1,4 @@ -#Customize Harbor token service with your key and certificate +# Customize Harbor token service with your key and certificate Harbor requires Docker client to access the Harbor registry with a token. The procedure to generate a token is like [Docker Registry v2 authentication](https://github.com/docker/distribution/blob/master/docs/spec/auth/token.md). Firstly, you should make a request to the token service for a token. The token is signed by the private key. After that, you make a new request with the token to the Harbor registry, Harbor registry will verify the token with the public key in the rootcert bundle. Then Harbor registry will authorize the Docker client to push/pull images. diff --git a/docs/developer_guide_i18n.md b/docs/developer_guide_i18n.md index 9a88ef8cd..0e90a75de 100644 --- a/docs/developer_guide_i18n.md +++ b/docs/developer_guide_i18n.md @@ -4,57 +4,55 @@ ### Steps to localize the UI in your language -1. Copy the file `static/resources/js/services/i18n/locale_messages_en-US.js` to a new file in the same directory named `locale_messages__.js` . +1. In the folder `src/ui_ng/src/i18n/lang`, copy json file `en-us-lang.json` to a new file and rename it to `--lang.json` . - The file contains a JSON object named `locale_messages`, which consists of key-value pairs of UI strings: + The file contains a JSON object including all the key-value pairs of UI strings: ``` - var local_messages = { - 'sign_in': 'Sign In', - 'sign_up': 'Sign Up', - ... - }; + { + "APP_TITLE": { + "VMW_HARBOR": "VMware Harbor", + "HARBOR": "Harbor", + ... + }, + ... + } ``` - In the file `locale_messages__.js`, translate all the values into your language. Do not change any keys. + In the file `--lang.json`, translate all the values into your language. Do not change any keys. -2. After creating your locale file, you should include it from the HTML page header template. +2. After creating your language file, you should add it to the language supporting list. - In the file `views/sections/header-include.htm`, look for a `if` statement which switch langauges based on the current language (`.Lang`) value. Add in a `else if` statement for your language: + Locate the file `src/ui_ng/src/app/shared/shared.const.ts`. + Append `-` to the language supporting list: ``` - {{ if eq .Lang "zh-CN" }} - - {{ else if eq .Lang "en-US"}} - - {{ else if eq .Lang "-"}} - - {{ end }} + export const supportedLangs = ['en-us', 'zh-cn', '-']; + ``` + Define the language display name and append it to the name list: + ``` + export const languageNames = { + "en-us": "English", + "zh-cn": "中文简体", + "-": "" + }; ``` -3. Add the new language to the `I18nService` module. - In the file `static/resources/js/services/i18n/services.i18n.js`, append a new key-value item to the `supportLanguages` object. This value will be displayed in the language dropdown list in the UI. - ``` - var supportLanguages = { - 'en-US': 'English', - 'zh-CN': '中文', - '-': '' - }; - ``` **NOTE: Don't miss the comma before the new key-value item you've added.** +3. Enable the new language in the view. -4. In the directory `static/i18n/`, copy the file `locale_en-US.ini` to a new file named `locale_-.ini`. In this file, translate all the values on the right hand side into your language. Do not change any keys. - -5. Add the new language to the `app.conf` file. - - In the file `make/common/templates/ui/app.conf`, append a new item to the configuration section. + Locate the file `src/ui_ng/src/app/base/navigator/navigator.component.html` and then find the following code piece: ``` - [lang] - types = en-US|zh-CN|- - names = en-US|zh-CN|- + + ``` + Add new menu item for your language: + ``` + ``` -6. Next, change to `make/` directory, rebuild and restart the Harbor by the below command: - ``` - docker-compose down - docker-compose up --build -d - ``` - \ No newline at end of file +4. Next, please refer [compile guideline](compile_guide.md) to rebuild and restart Harbor. diff --git a/docs/expand_hard_disk.md b/docs/expand_hard_disk.md deleted file mode 100644 index 063fed88d..000000000 --- a/docs/expand_hard_disk.md +++ /dev/null @@ -1,102 +0,0 @@ -# Expand the Hard Disk of Virtual Appliance - -If you install Harbor with OVA, the persistent data(such as images and database) is stored in a hard disk which is mounted on directory "/data", and the default size is 50GB. As more and more images are pushed into it, the capacity may not meet your requirements. - -You can check the space on Harbor web UI by clicking on the admin's name at the upper left corner and selecting "About" from the drop-down menu if you log in with an admin user: - -![lvm](img/lvm/check_on_ui_01.png) - -If your free space is running out, you can expand the size of the hard disk by the following steps: - -1. Add New Hard Disk to VM - - (1) Log in vSphere web client. Power off Harbor's virtual appliance. - (2) Right click on the VM and select "Edit Settings". - (3) Select "New Hard Disk", and click "OK". - - ![lvm](img/lvm/add_new_hard_disk.png) - - We add a 10GB new hard disk to show the operations. - - (4) Power on the VM. - -2. Expand Hard Disk using LVM - - Login from the console of the virtual appliance and run the following commands: - - (1) Check the current size of "/data": - ```sh - df -h /data - ``` - - ![lvm](img/lvm/size_of_data_01.png) - - (2) Find the new hard disk, e.g. "/dev/sdc". Replace all "/dev/sdc" with your disk in the following commands. - ```sh - fdisk -l - ``` - - ![lvm](img/lvm/find_the_new_harddisk.png) - - (3) Create new physical volume: - ```sh - pvcreate /dev/sdc - ``` - - (4) Check the volume group: - ```sh - vgdisplay - ``` - - ![lvm](img/lvm/vg_01.png) - - (5) Expand the volume group: - ```sh - vgextend data1_vg /dev/sdc - ``` - - (6) Check the volume group again: - ```sh - vgdisplay - ``` - - ![lvm](img/lvm/vg_02.png) - - (7) Check the logical volume: - ```sh - lvdisplay - ``` - - ![lvm](img/lvm/lv_01.png) - - (8) Resize the logical volume: - ```sh - lvresize -l +100%FREE /dev/data1_vg/data - ``` - - ![lvm](img/lvm/resize_lv.png) - - (9) Check the logical volume again, note the change of "LV Size": - ```sh - lvdisplay - ``` - - ![lvm](img/lvm/lv_02.png) - - (10) Resize the file system: - ```sh - resize2fs /dev/data1_vg/data - ``` - - (11) Check the size "/data" again: - ```sh - df -h /data - ``` - - ![lvm](img/lvm/size_of_data_02.png) - - You can also check the size on Harbor web UI: - - ![lvm](img/lvm/check_on_ui.png) - -After that, your disk should be expanded successfully. If you want to add more hard disks, do the steps again. \ No newline at end of file diff --git a/docs/img/content_trust.png b/docs/img/content_trust.png new file mode 100644 index 000000000..541c4f93d Binary files /dev/null and b/docs/img/content_trust.png differ diff --git a/docs/img/ldap_auth.png b/docs/img/ldap_auth.png new file mode 100644 index 000000000..32bed1d29 Binary files /dev/null and b/docs/img/ldap_auth.png differ diff --git a/docs/img/new_add_member.png b/docs/img/new_add_member.png index c7e0bbf03..ed014883c 100644 Binary files a/docs/img/new_add_member.png and b/docs/img/new_add_member.png differ diff --git a/docs/img/new_auth.png b/docs/img/new_auth.png new file mode 100644 index 000000000..504f0b6d4 Binary files /dev/null and b/docs/img/new_auth.png differ diff --git a/docs/img/new_browse_project.png b/docs/img/new_browse_project.png index d70a65674..a57b074c2 100644 Binary files a/docs/img/new_browse_project.png and b/docs/img/new_browse_project.png differ diff --git a/docs/img/new_config_email.png b/docs/img/new_config_email.png new file mode 100644 index 000000000..829444eee Binary files /dev/null and b/docs/img/new_config_email.png differ diff --git a/docs/img/new_config_token.png b/docs/img/new_config_token.png new file mode 100644 index 000000000..f8e2343bc Binary files /dev/null and b/docs/img/new_config_token.png differ diff --git a/docs/img/new_create_project.png b/docs/img/new_create_project.png index dea724fbc..fc5388cde 100644 Binary files a/docs/img/new_create_project.png and b/docs/img/new_create_project.png differ diff --git a/docs/img/new_create_rule.png b/docs/img/new_create_rule.png new file mode 100644 index 000000000..31912e62e Binary files /dev/null and b/docs/img/new_create_rule.png differ diff --git a/docs/img/new_delete_repo.png b/docs/img/new_delete_repo.png new file mode 100644 index 000000000..e987e928e Binary files /dev/null and b/docs/img/new_delete_repo.png differ diff --git a/docs/img/new_delete_tag.png b/docs/img/new_delete_tag.png new file mode 100644 index 000000000..2a6df4f95 Binary files /dev/null and b/docs/img/new_delete_tag.png differ diff --git a/docs/img/new_manage_endpoint.png b/docs/img/new_manage_endpoint.png new file mode 100644 index 000000000..14ef73eff Binary files /dev/null and b/docs/img/new_manage_endpoint.png differ diff --git a/docs/img/new_manage_replication.png b/docs/img/new_manage_replication.png index 4fc0a9f24..9fc40af92 100644 Binary files a/docs/img/new_manage_replication.png and b/docs/img/new_manage_replication.png differ diff --git a/docs/img/new_proj_create.png b/docs/img/new_proj_create.png new file mode 100644 index 000000000..cf9c4ff61 Binary files /dev/null and b/docs/img/new_proj_create.png differ diff --git a/docs/img/new_project_log.png b/docs/img/new_project_log.png index d2bdb5530..49a602c1e 100644 Binary files a/docs/img/new_project_log.png and b/docs/img/new_project_log.png differ diff --git a/docs/img/new_remote_cert.png b/docs/img/new_remote_cert.png new file mode 100644 index 000000000..c475b6a80 Binary files /dev/null and b/docs/img/new_remote_cert.png differ diff --git a/docs/img/new_remove_update_member.png b/docs/img/new_remove_update_member.png index 5ba4465f1..08bffa0b9 100644 Binary files a/docs/img/new_remove_update_member.png and b/docs/img/new_remove_update_member.png differ diff --git a/docs/img/new_rule_list.png b/docs/img/new_rule_list.png new file mode 100644 index 000000000..d49b7526f Binary files /dev/null and b/docs/img/new_rule_list.png differ diff --git a/docs/img/new_search.png b/docs/img/new_search.png index e9cb5e8b4..4fe309d49 100644 Binary files a/docs/img/new_search.png and b/docs/img/new_search.png differ diff --git a/docs/img/new_self_reg.png b/docs/img/new_self_reg.png new file mode 100644 index 000000000..3c27f24a0 Binary files /dev/null and b/docs/img/new_self_reg.png differ diff --git a/docs/img/new_set_admin_remove_user.png b/docs/img/new_set_admin_remove_user.png index 9b2a248e5..b2c77eed9 100644 Binary files a/docs/img/new_set_admin_remove_user.png and b/docs/img/new_set_admin_remove_user.png differ diff --git a/docs/installation_guide.md b/docs/installation_guide.md index 5fd885df0..587665a45 100644 --- a/docs/installation_guide.md +++ b/docs/installation_guide.md @@ -1,21 +1,18 @@ # Installation and Configuration Guide -Harbor can be installed by one of three approaches: +Harbor can be installed by one of two approaches: - **Online installer:** The installer downloads Harbor's images from Docker hub. For this reason, the installer is very small in size. - **Offline installer:** Use this installer when the host does not have an Internet connection. The installer contains pre-built images so its size is larger. -- **Virtual Appliance:** If you are installing Harbor as the registry component of vSphere Integrated Containers (VIC), or using Harbor as a standalone registry on vSphere platform, download the OVA version of Harbor. All installers can be downloaded from the **[official release](https://github.com/vmware/harbor/releases)** page. -To install Harbor's virtual appliance, refer to the **[Harbor Installation Guide for Virtual Appliance](installation_guide_ova.md)**. - This guide describes the steps to install and configure Harbor by using the online or offline installer. The installation processes are almost the same. If you run a previous version of Harbor, you may need to migrate the data to fit the new database schema. For more details, please refer to **[Data Migration Guide](migration_guide.md)**. -In addition, the deployment instructions on Kubernetes has been created by the community. Refer to set up [Harbor on Kubernetes](kubernetes_deployment.md) for details. +In addition, the deployment instructions on Kubernetes has been created by the community. Refer to [Harbor on Kubernetes](kubernetes_deployment.md) for details. ## Prerequisites for the target host Harbor is deployed as several Docker containers, and, therefore, can be deployed on any Linux distribution that supports Docker. The target host requires Python, Docker, and Docker Compose to be installed. @@ -47,17 +44,32 @@ Offline installer: #### Configuring Harbor Configuration parameters are located in the file **harbor.cfg**. + +There are two categories of parameters in harbor.cfg, **required parameters** and **optional parameters**. +* **required parameters**: These parameters are required to be set in the configuration file, and they will take effect if a user updates them in harbor.cfg, rerun the ```install.sh``` script to reinstall Harbor. +* **optional parameters**: These parameters are optional, and only take effect in the initial installation. The user can leave them blank and update them on Web UI after Harbor is started. Subsequent update to these parameters in ```harbor.cfg``` will be ignored. + The parameters are described below - note that at the very least, you will need to change the **hostname** attribute. +##### Required parameters: + * **hostname**: The target host's hostname, which is used to access the UI and the registry service. It should be the IP address or the fully qualified domain name (FQDN) of your target machine, e.g., `192.168.1.10` or `reg.yourdomain.com`. _Do NOT use `localhost` or `127.0.0.1` for the hostname - the registry service needs to be accessible by external clients!_ -* **ui_url_protocol**: (**http** or **https**. Default is **http**) The protocol used to access the UI and the token/notification service. By default, this is _http_. To set up the https protocol, refer to **[Configuring Harbor with HTTPS Access](configure_https.md)**. +* **ui_url_protocol**: (**http** or **https**. Default is **http**) The protocol used to access the UI and the token/notification service. If Notary is enabled, this parameter has to be _https_. By default, this is _http_. To set up the https protocol, refer to **[Configuring Harbor with HTTPS Access](configure_https.md)**. +* **db_password**: The root password for the MySQL database used for **db_auth**. _Change this password for any production use!_ +* **max_job_workers**: (default value is **3**) The maximum number of replication workers in job service. For each image replication job, a worker synchronizes all tags of a repository to the remote destination. Increasing this number allows more concurrent replication jobs in the system. However, since each worker consumes a certain amount of network/CPU/IO resources, please carefully pick the value of this attribute based on the hardware resource of the host. +* **customize_crt**: (**on** or **off**. Default is **on**) When this attribute is **on**, the prepare script creates private key and root certificate for the generation/verification of the registry's token. Set this attribute to **off** when the key and root certificate are supplied by external sources. Refer to [Customize Key and Certificate of Harbor Token Service](customize_token_service.md) for more info. +* **ssl_cert**: The path of SSL certificate, it's applied only when the protocol is set to https +* **ssl_cert_key**: The path of SSL key, it's applied only when the protocol is set to https +* **secretkey_path**: The path of key for encrypt or decrypt the password of a remote registry in a replication policy. + +##### Optional parameters * **Email settings**: These parameters are needed for Harbor to be able to send a user a "password reset" email, and are only necessary if that functionality is needed. Also, do note that by default SSL connectivity is _not_ enabled - if your SMTP server requires SSL, but does _not_ support STARTTLS, then you should enable SSL by setting **email_ssl = true**. - * email_server = smtp.mydomain.com - * email_server_port = 25 - * email_username = sample_admin@mydomain.com - * email_password = abc - * email_from = `admin \` - * email_ssl = false + * email_server = smtp.mydomain.com + * email_server_port = 25 + * email_username = sample_admin@mydomain.com + * email_password = abc + * email_from = admin + * email_ssl = false * **harbor_admin_password**: The administrator's initial password. This password only takes effect for the first time Harbor launches. After that, this setting is ignored and the administrator's password should be set in the UI. _Note that the default username/password are **admin/Harbor12345** ._ * **auth_mode**: The type of authentication that is used. By default, it is **db_auth**, i.e. the credentials are stored in a database. For LDAP authentication, set this to **ldap_auth**. @@ -68,15 +80,10 @@ The parameters are described below - note that at the very least, you will need * **ldap_filter**:The search filter for looking up a user, e.g. `(objectClass=person)`. * **ldap_uid**: The attribute used to match a user during a LDAP search, it could be uid, cn, email or other attributes. * **ldap_scope**: The scope to search for a user, 1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE. Default is 3. -* **db_password**: The root password for the MySQL database used for **db_auth**. _Change this password for any production use!_ -* **self_registration**: (**on** or **off**. Default is **on**) Enable / Disable the ability for a user to register themselves. When disabled, new users can only be created by the Admin user, only an admin user can create new users in Harbor. _NOTE: When **auth_mode** is set to **ldap_auth**, self-registration feature is **always** disabled, and this flag is ignored._ -* **use_compressed_js**: (**on** or **off**. Default is **on**) For production use, turn this flag to **on**. In development mode, set it to **off** so that js files can be modified separately. -* **max_job_workers**: (default value is **3**) The maximum number of replication workers in job service. For each image replication job, a worker synchronizes all tags of a repository to the remote destination. Increasing this number allows more concurrent replication jobs in the system. However, since each worker consumes a certain amount of network/CPU/IO resources, please carefully pick the value of this attribute based on the hardware resource of the host. - +* **self_registration**: (**on** or **off**. Default is **on**) Enable / Disable the ability for a user to register himself/herself. When disabled, new users can only be created by the Admin user, only an admin user can create new users in Harbor. _NOTE: When **auth_mode** is set to **ldap_auth**, self-registration feature is **always** disabled, and this flag is ignored._ * **token_expiration**: The expiration time (in minutes) of a token created by token service, default is 30 minutes. - +* **project_creation_restriction**: The flag to control what users have permission to create projects. By default everyone can create a project, set to "adminonly" such that only admin can create project. * **verify_remote_cert**: (**on** or **off**. Default is **on**) This flag determines whether or not to verify SSL/TLS certificate when Harbor communicates with a remote registry instance. Setting this attribute to **off** bypasses the SSL/TLS verification, which is often used when the remote instance has a self-signed or untrusted certificate. -* **customize_crt**: (**on** or **off**. Default is **on**) When this attribute is **on**, the prepare script creates private key and root certificate for the generation/verification of the registry's token. The following attributes:**crt_country**, **crt_state**, **crt_location**, **crt_organization**, **crt_organizationalunit**, **crt_commonname**, **crt_email** are used as parameters for generating the keys. Set this attribute to **off** when the key and root certificate are supplied by external sources. Refer to [Customize Key and Certificate of Harbor Token Service](customize_token_service.md) for more info. #### Configuring storage backend (optional) @@ -103,6 +110,9 @@ _NOTE: For detailed information on storage backend of a registry, refer to [Regi #### Finishing installation and starting Harbor Once **harbor.cfg** and storage backend (optional) are configured, install and start Harbor using the ```install.sh``` script. Note that it may take some time for the online installer to download Harbor images from Docker hub. +##### Default installation +After version 1.1.0, Harbor has integrated with Notary, but by default the installation does not include notary support. + ```sh $ sudo ./install.sh ``` @@ -116,6 +126,16 @@ $ docker push reg.yourdomain.com/myproject/myrepo:mytag ``` **IMPORTANT:** The default installation of Harbor uses _HTTP_ - as such, you will need to add the option `--insecure-registry` to your client's Docker daemon and restart the Docker service. +##### Installation with Notary +To install Harbor with Notary support, add a parameter when you run ```install.sh``` +```sh + $ sudo ./install.sh --with-notary +``` +**Note**: For installation with Notary the parameter "ui_url_protocol" must be set to "https", for configuring HTTPS certificate please refer to the following sections. + +More information about Notary and Docker Content Trust, please refer to docker's documentation: +https://docs.docker.com/engine/security/trust/content_trust/ + For information on how to use Harbor, please refer to **[User Guide of Harbor](user_guide.md)** . #### Configuring Harbor with HTTPS access @@ -146,18 +166,17 @@ Starting registry ... done Starting proxy ... done ``` -To change Harbor's configuration, first stop existing Harbor instance, update harbor.cfg, and then run install.sh again: +To change Harbor's configuration, first stop existing Harbor instance, update harbor.cfg, and then run prepare script to populate the configuration, and then re-create and start Harbor's instance: ``` -$ sudo docker-compose down - +$ sudo docker-compose down -v $ vim harbor.cfg - -$ sudo install.sh +$ sudo prepare +$ sudo docker-compose up -d ``` Removing Harbor's containers while keeping the image data and Harbor's database files on the file system: ``` -$ sudo docker-compose down +$ sudo docker-compose down -v ``` Removing Harbor's database and image data (for a clean re-installation): @@ -166,6 +185,20 @@ $ rm -r /data/database $ rm -r /data/registry ``` +#### _Managing lifecycle of Harbor when it's installed with Notary_ + +When Harbor is installed with Notary, user needs to add extra template file ```docker-compose.notary.yml``` to docker-compose command, so the docker-compose commands to manage the lifecycle of Harbor will be: +``` +$ sudo docker-compose -f ./docker-compose.yml -f ./docker-compose.notary.yml [ up|down|ps|stop|start ] +``` +For example, if user want's to change ```harbor.cfg``` and re-deploy Harbor when it's installed with Notary, the following commands should be used: +```sh +$ sudo docker-compose -f ./docker-compose.yml -f ./docker-compose.notary.yml down -v +$ vim harbor.cfg +$ sudo prepare --with-notary +$ sudo docker-compose -f ./docker-compose.yml -f ./docker-compose.notary.yml up -d +``` + Please check the [Docker Compose command-line reference](https://docs.docker.com/compose/reference/) for more on docker-compose. ### Persistent data and log files @@ -202,27 +235,17 @@ proxy: tag: "proxy" ``` -2.Modify templates/registry/config.yml -Add the customized port, e.g. ":8888", after "$ui_url". +2.Modify harbor.cfg, add the port to the parameter "hostname" ``` -auth: - token: - issuer: registry-token-issuer - realm: $ui_url:8888/service/token - rootcertbundle: /etc/registry/root.crt - service: token-service +hostname = 192.168.0.2:8888 ``` -3.Run install.sh to update and start Harbor. -```sh -$ sudo docker-compose down -$ sudo install.sh -``` +3.Re-deploy Harbor refering to previous section "Managing Harbor's lifecycle". ### For HTTPS protocol 1.Enable HTTPS in Harbor by following this [guide](https://github.com/vmware/harbor/blob/master/docs/configure_https.md). 2.Modify docker-compose.yml -Replace the first "443" to a customized port, e.g. 4443:443. +Replace the first "443" to a customized port, e.g. 8888:443. ``` proxy: @@ -232,7 +255,7 @@ proxy: - ./config/nginx:/etc/nginx ports: - 80:80 - - 4443:443 + - 8888:443 depends_on: - mysql - registry @@ -245,23 +268,14 @@ proxy: tag: "proxy" ``` -3.Modify templates/registry/config.yml -Add the customized port, e.g. ":4443", after "$ui_url". +3.Modify harbor.cfg, add the port to the parameter "hostname" ``` -auth: - token: - issuer: registry-token-issuer - realm: $ui_url:4443/service/token - rootcertbundle: /etc/registry/root.crt - service: token-service +hostname = 192.168.0.2:8888 ``` -4.Run install.sh to update and start Harbor. -```sh -$ sudo docker-compose down -$ sudo install.sh -``` +4.Re-deploy Harbor refering to previous section "Managing Harbor's lifecycle". + ## Troubleshooting 1. When Harbor does not work properly, run the below commands to find out if all containers of Harbor are in **UP** status: @@ -271,7 +285,7 @@ $ sudo install.sh ----------------------------------------------------------------------------------------------------- harbor-db docker-entrypoint.sh mysqld Up 3306/tcp harbor-jobservice /harbor/harbor_jobservice Up - harbor-log /bin/sh -c crond && rsyslo ... Up 0.0.0.0:1514->514/tcp + harbor-log /bin/sh -c crond && rsyslo ... Up 127.0.0.1:1514->514/tcp harbor-ui /harbor/harbor_ui Up nginx nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp registry /entrypoint.sh serve /etc/ ... Up 5000/tcp @@ -283,9 +297,4 @@ If a container is not in **UP** state, check the log file of that container in d ``` proxy_set_header X-Forwarded-Proto $scheme; ``` -And run the following commands to restart Harbor: -```sh -$ sudo docker-compose down -$ sudo ./prepare -$ sudo docker-compose up -d -``` +and re-deploy Harbor refer to the previous section "Managing Harbor's lifecycle". diff --git a/docs/installation_guide_ova.md b/docs/installation_guide_ova.md deleted file mode 100644 index 82f01c3c0..000000000 --- a/docs/installation_guide_ova.md +++ /dev/null @@ -1,199 +0,0 @@ -# Installing and Configuring Harbor on vSphere as Virtual Appliance - -* [Prerequisites](#prerequisites) -* [Planning for installation](#planning-for-installation) -* [Installation](#installation) -* [Getting Certificate of Harbor's CA](#getting-certificate-of-harbors-ca) -* [Reconfiguration](#reconfiguration) -* [Troubleshooting](#troubleshooting) - -This guide walks you through the steps about installing and configuring Harbor on vSphere as a virtual appliance. If you are installing Harbor on a Linux host, refer to this **[Installation Guide](installation_guide.md)**. - -## Prerequisites -* vCenter 5.5+ and at least an ESX host. -* 2 vCPUs, 4GB memory and 80GB free disk space in datastore. -* A network with DHCP capability, or a static IP address for the virtual appliance. - -## Planning for installation - -### User management -By default, Harbor stores user information in an internal database. Harbor can also be configured to authenticate against an external LDAP or AD server. For LDAP/AD authentication, the **Authentication Mode** property must be set to *ldap_auth* at the deployment time. - -**NOTE:** This mode cannot be changed after the first boot of Harbor. - -### Security - -Harbor uses HTTPS for secure communication by default. A self-signed certificate is generated at first boot based on its FQDN (Fully Qualified Domain Name) or IP address. A Docker client or a VCH (Virtual Container Host) needs to trust the certificate of Harbor's CA (Certificate Authority) in order to interact with Harbor. - -Harbor always tries to generate a self-signed certificate based on its FQDN. Therefore, its IP address must have a FQDN associated with it in the DNS server. If Harbor cannot resolve its IP address to a FQDN, it generates the self-signed certificate using its IP address. In this case, Harbor can only be accessed by IP address. When Harbor's IP address or FQDN is changed, the self-signed certificate will be re-generated after a reboot. - -Harbor's self-generated certificate can be replaced by supplying a certificate signed by other CAs in OVA's settings. - -Harbor can be configured to use plain HTTP for some environments such as testing and continuous integration (CI). However, it is **NOT** recommended to use HTTP for production because the communication is never secure. - -### Networking - -Harbor can obtain IP address by DHCP. This is convenient for testing purpose. For a production system, it is recommended that static IP address and FQDN be used. - -For the purpose of generating a self-signed certificate, it is recommended that a DNS record be added to associate Harbor's IP address with a FQDN. This is necessary for both static IP address and dynamic IP address acquired from DHCP. If a DNS record is missing for Harbor's IP address, Harbor can only be accessed by its IP address. - - -## Installation -1. Download the OVA file to your local disk from the **[official release page](https://github.com/vmware/harbor/releases)**. - -2. Log in vSphere web client. Right click on the datacenter, cluster or host which Harbor will be deployed on. Select "Deploy OVF Template" and open the import wizard. - - ![ova](img/ova/ova01.png) - -3. Select the OVA file from your local disk and click "Next". - - ![ova](img/ova/ova02.png) - -4. Review the OVF template details and click "Next". - - ![ova](img/ova/ova03.png) - -5. Accept the end user license agreements and click "Next". - - ![ova](img/ova/ova04.png) - -6. Specify a name and a location for the virtual appliance. - - ![ova](img/ova/ova05.png) - -7. Select the datastore and virtual disk format, click "Next". - - ![ova](img/ova/ova06.png) - -8. Configure the network(s) that the virtual appliance should be connected to. - - ![ova](img/ova/ova07.png) - -9. Customize the properties of Harbor. The properties are described below. Note that at the very least, you just need to set four properties: **Root Password**, **Harbor Admin Password**, **Database Password** and **Authentication Mode**. - - ![ova](img/ova/ova08.png) - - * System - * **Root Password**: The initial password of the root user. Subsequent changes of password should be performed in operating system. (8-128 characters) - * **Harbor Admin Password**: The initial password of Harbor admin. It only works for the first time when Harbor starts. It has no effect after the first launch of Harbor. Change the admin password from UI after launching Harbor. (8-20 characters) - * **Database Password**: The initial password of the root user of MySQL database. Subsequent changes of password should be performed in operating system. (8-128 characters) - * **Permit Root Login**: Specifies whether root user can log in using SSH. - * **Garbage Collection**: When setting this to true, Harbor performs garbage collection everytime it boots up. The first time setting this flag to true needs to power off the VM and power it on again. - - * Authentication - - The **Authentication Mode** must be set before the first boot of Harbor. Subsequent changes to **Authentication Mode** do not have any effect. When **ldap_auth** mode is enabled, properties related to LDAP/AD must be set. - - * **Authentication Mode**: The default authentication mode is **db_auth**. Set it to **ldap_auth** when users' credentials are stored in an LDAP or AD server. Note: this option can only be set once. - * **Self Registration**: Determine whether the self-registration is allowed or not. Set this to off to disable a user's self-registration in Harbor. This flag has no effect when users are stored in LDAP or AD. - * **LDAP URL**: The URL of an LDAP/AD server. - * **LDAP Search DN**: A user's DN who has the permission to search the LDAP/AD server. Leave blank if your LDAP/AD server supports anonymous search, otherwise you should configure this DN and **LDAP Search Password**. - * **LDAP Search Password**: The password of the user for LDAP search. Leave blank if your LDAP/AD server supports anonymous search. - * **LDAP Base DN**: The base DN of a node from which to look up a user for authentication. The search scope includes subtree of the node. - * **LDAP UID**: The attribute used in a search to match a user, it could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD server. - - * Security - - If HTTPS is enabled, a self-signed certificate is generated by default. To supply your own certificate, fill in two properties: **SSL Cert** and **SSL Cert Key**. Do not use HTTP in any production system. **Notes:** If you want to enable HTTPS with your own self-signed certificate, refer to the "Getting a certificate" section of this **[guide](https://github.com/vmware/harbor/blob/master/docs/configure_https.md#getting-a-certificate)** for more details. - - * **Protocol**: The protocol for accessing Harbor. Warning: setting it to http makes the communication insecure. - * **SSL Cert**: Paste in the content of a certificate file. Leave blank for a generated self-signed certificate. - * **SSL Cert Key**: Paste in the content of a certificate key file. Leave blank for a generated key. - * **Verify Remote Cert**: Determine whether the image replication should verify the certificate of a remote Harbor registry. Set this flag to off when the remote registry uses a self-signed or untrusted certificate. - - * Email Settings - - To allow a user to reset his/her own password through email, configure the below email settings: - - * **Email Server**: The mail server to send out emails to reset password. - * **Email Server Port**: The port of mail server. - * **Email Username**: The user from whom the password reset email is sent. Usually this is a system email address. - * **Email Password**: The password of the user from whom the password reset email is sent. - * **Email From**: The name of the email sender. - * **Email SSL**: Whether to enable secure mail transmission. - - * Networking properties - * **Default Gateway**: The default gateway address for this VM. Leave blank if DHCP is desired. - * **Domain Name**: The domain name of this VM. Run command `man resolv.conf` for more explanation. Leave blank if DHCP is desired or the domain name is not needed for static IP. - * **Domain Search Path**: The domain search path(comma or space separated domain names) for this VM. Leave blank if DHCP is desired. - * **Domain Name Servers**: The domain name server IP Address for this VM(comma separated). Leave blank if DHCP is desired. - * **Network 1 IP Address**: The IP address of this interface. Leave blank if DHCP is desired. - * **Network 1 Netmask**: The netmask or prefix for this interface. Leave blank if DHCP is desired. - - After you complete the properties, click "Next". - -10. Review your settings and click "Finish" to complete the deployment. - - ![ova](img/ova/ova09.png) - -11. Power on the virtual appliance. It may take a few minutes for the first bootup. The virtual appliance needs to initialize itself for configuration like network address and password. - -12. When the appliance is ready, check from vSphere Web Client for its IP address. Open a browser and type in the URL `http(s)://harbor_ip_address` or `http(s)://harbor_host_name`. Log in as the admin user and verify Harbor has been successfully installed. - -13. For information on how to use Harbor, please refer to [User Guide of Harbor Virtual Appliance](user_guide_ova.md). - -## Getting Certificate of Harbor's CA - -By default, Harbor uses a self-signed certificate in HTTPS. A Docker client or a VCH needs to trust the self-signed certificate of Harbor's CA in order to interact with Harbor. -To download the certificate of Harbor's CA and import into a Docker client, follow the below steps. If a certificate issued by a public known CA is used, the below steps are not needed. - -1. Log in Harbor's UI as an admin user. -2. Click on the admin's name at the upper left corner and select **"About"** from the drop-down menu. -3. Click on the **Download** link to save the certificate file as `ca.crt`. - - ![ova](img/ova/downloadcert.png) - -4. Copy the certificate file `ca.crt` to a Docker host. To access Harbor using its FQDN, run the below commands, replace `` with the actual FQDN of the Harbor instance: - ``` - mkdir -p /etc/docker/certs.d/ - cp ca.crt /etc/docker/certs.d// - ``` - To access Harbor using its IP address, run the below commands, replace `` with the actual IP address of the Harbor instance: - ``` - mkdir -p /etc/docker/certs.d/ - cp ca.crt /etc/docker/certs.d// - ``` - - **Note:** If you run both of the above two sets of commands, Harbor can be accessed by either FQDN or IP address. - -5. Run `docker login` command to verify that HTTPS is working. - -To import the CA's certificate into VCH, complete Step 1-3 and refer to VCH's document for instructions. - -## Reconfiguration -If you want to change the properties of Harbor, follow the below steps: - -1. **Power off** Harbor's virtual appliance. -2. Right click on the VM and select "Edit Settings". - - ![ova](img/ova/edit_settings.png) - -3. Click the "vApp Options" tab, update the properties and click "OK". - - ![ova](img/ova/vapp_options.png) - -4. **Power on** the VM and Harbor will reconfigure itself based on the new settings. - -**Note:** -1. The **Authentication Mode** can only be set once before the first boot. Subsequent modification of this option does not have any effect. -2. The initial admin password, root password of the virtual appliance, MySQL root password, and all networking properties can not be modified using this method after Harbor's first launch. Modify them by the following approach: - * **Harbor Admin Password**: Change it in Harbor admin portal. - * **Root Password of Virtual Appliance**: Change it by logging in the virtual appliance and doing it in the Linux operating system. - * **MySQL Root Password**: Change it by logging in the virtual appliance and doing it in the Linux operating system. - * **Networking Properties**: Visit `https://harbor_ip_address:5480`, log in with root/password of your virtual appliance and modify networking properties. Reboot the system after modification to ensure Harbor's self-signed certificate gets updated. - -## Troubleshooting - -### Log collection -For diagnosis purpose, logs can be collected by the following steps: - -1. Log in the operating system of Harbor virtual appliance and run the below command: - ```sh - /harbor/script/collect.sh - ``` - A "harbor_logs.tar.gz" file is generated in the current directory. - -2. Copy the log file to other host and send to your administrator, replace the `` , `` and `` with proper values: - ```sh - scp ./harbor_logs.tar.gz @: - ``` \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d516872b4..3101e5d8a 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,5 +1,3 @@ -# this is an example of the Uber API -# as a demonstration of an API spec in YAML swagger: '2.0' info: title: Harbor API @@ -517,7 +515,6 @@ paths: responses: 200: description: Get current user information successfully. - in: body schema: $ref: '#/definitions/User' 401: @@ -659,6 +656,11 @@ paths: format: int32 required: true description: Relevant project ID. + - name: detail + in: query + type: boolean + required: false + description: Get detail info or not. - name: q in: query type: string @@ -680,11 +682,11 @@ paths: - Products responses: 200: - description: Searched for respositories successfully. + description: If detail is false, the response body is a string array which contains the names of repositories, or the response body contains an object array as described in schema. schema: type: array items: - type: string + $ref: '#/definitions/Repository' headers: X-Total-Count: description: The total count of repositories @@ -700,26 +702,27 @@ paths: description: Project ID does not exist. 500: description: Unexpected internal errors. + /repositories/{repo_name}/tags/{tag}: delete: - summary: Delete a repository or a tag in a repository. + summary: Delete a tag in a repository. description: | - This endpoint let user delete repositories and tags with repo name and tag. + This endpoint let user delete tags with repo name and tag. parameters: - name: repo_name - in: query + in: path type: string required: true description: The name of repository which will be deleted. - name: tag - in: query + in: path type: string - required: false + required: true description: Tag of a repository. tags: - Products responses: 200: - description: Delete repository or tag successfully. + description: Delete tag successfully. 400: description: Invalid repo_name. 401: @@ -728,41 +731,69 @@ paths: description: Repository or tag not found. 403: description: Forbidden. - /repositories/tags: + /repositories/{repo_name}/tags: get: summary: Get tags of a relevant repository. description: | This endpoint aims to retrieve tags from a relevant repository. parameters: - name: repo_name - in: query + in: path type: string required: true description: Relevant repository name. + - name: detail + in: query + type: boolean + required: false + description: If detail is true, the manifests is returned too. tags: - Products responses: 200: - description: Retrieved tags from a relevant repository successfully. + description: If detail is false, the response body is a string array, or the response body contains the manifest informations as described in schema. schema: type: array items: - type: string + $ref: '#/definitions/DetailedTag' 500: description: Unexpected internal errors. - /repositories/manifests: + delete: + summary: Delete all tags of a repository. + description: | + This endpoint let user delete all tags with repo name. + parameters: + - name: repo_name + in: path + type: string + required: true + description: The name of repository which will be deleted. + tags: + - Products + responses: + 200: + description: Delete successfully. + 400: + description: Invalid repo_name. + 401: + description: Unauthorized. + 404: + description: Repository not found. + 403: + description: Forbidden. + /repositories/{repo_name}/tags/{tag}/manifest: get: summary: Get manifests of a relevant repository. description: | This endpoint aims to retreive manifests from a relevant repository. parameters: - name: repo_name - in: query + in: path type: string required: true description: Repository name - name: tag - in: query + in: path type: string required: true description: Tag name @@ -777,11 +808,37 @@ paths: 200: description: Retrieved manifests from a relevant repository successfully. schema: - $ref: '#/definitions/Repository' + $ref: '#/definitions/Manifest' 404: description: Retrieved manifests from a relevant repository not found. 500: description: Unexpected internal errors. + /repositories/{repo_name}/signatures: + get: + summary: Get signature information of a repository + description: | + This endpoint aims to retrieve signature information of a repository, the data is + from the nested notary instance of Harbor. + If the repository does not have any signature information in notary, this API will + return an empty list with response code 200, instead of 404 + parameters: + - name: repo_name + in: path + type: string + required: true + description: repository name. + tags: + - Products + responses: + 200: + description: Retrieved signatures. + schema: + type: array + items: + $ref: '#/definitions/RepoSignature' + 500: + description: Server side error. + /repositories/top: get: summary: Get public repositories which are accessed most. @@ -794,15 +851,20 @@ paths: format: int32 required: false description: The number of the requested public repositories, default is 10 if not provided. + - name: detail + in: query + type: boolean + required: false + description: Get detail info or not. tags: - Products responses: 200: - description: Retrieved top repositories successfully. + description: If detail is true, the response is described as the schema, or the response contains a TopRepo array. schema: type: array items: - $ref: '#/definitions/TopRepo' + $ref: '#/definitions/Repository' 400: description: Bad request because of invalid count. 500: @@ -1168,12 +1230,12 @@ paths: description: | This endpoint is for ping validates whether the target is reachable and whether the credential is valid. parameters: - - name: id - in: query - type: integer - format: int64 - required: false - description: The replication's policy ID. + - name: target + in: body + description: The target object. + required: true + schema: + $ref: '#/definitions/PingTarget' tags: - Products responses: @@ -1187,6 +1249,31 @@ paths: description: Target not found. 500: description: Unexpected internal errors. + /targets/{id}/ping: + post: + summary: Ping target. + description: | + This endpoint is for ping target. + parameters: + - name: id + in: path + type: integer + format: int64 + required: true + description: The replication's target ID. + tags: + - Products + responses: + 200: + description: Ping replication's target successfully. + 400: + description: Can not ping target. + 401: + description: User need to log in first. + 404: + description: Target ID does not exist. + 500: + description: Unexpected internal errors. /targets/{id}: put: summary: Update replication's target. @@ -1203,7 +1290,7 @@ paths: in: body required: true schema: - $ref: '#/definitions/RepTargetPost' + $ref: '#/definitions/PutTarget' description: Updates of replication's target. tags: - Products @@ -1309,7 +1396,229 @@ paths: 403: description: User does not have permission of admin role. 500: - description: Unexpected internal errors. + description: Unexpected internal errors. + /systeminfo: + get: + summary: Get general system info + description: | + This API is for retrieving general system info, this can be called by anonymous request. + tags: + - Products + responses: + 200: + description: Get general info successfully. + schema: + type: object + items: + $ref: "#/definitions/GeneralInfo" + 500: + description: Unexpected internal error. + /systeminfo/volumes: + get: + summary: Get system volume info (total/free size). + description: | + This endpoint is for retrieving system volume info that only provides for admin user. + tags: + - Products + responses: + 200: + description: Get system volumes successfully. + schema: + type: object + items: + $ref: '#/definitions/SystemInfo' + 401: + description: User need to log in first. + 403: + description: User does not have permission of admin role. + 500: + description: Unexpected internal errors. + /systeminfo/getcert: + get: + summary: Get default root certificate under OVA deployment. + description: | + This endpoint is for downloading a default root certificate that only provides for admin user under OVA deployment. + tags: + - Products + responses: + 200: + description: Get default root certificate successfully. + 401: + description: User need to log in first. + 403: + description: User does not have permission of admin role. + 404: + description: Not found the default root certificate. + 500: + description: Unexpected internal errors. + /ldap/ping: + post: + summary: Ping available ldap service. + description: | + This endpoint ping the available ldap service for test related configuration parameters. + parameters: + - name: ldapconf + in: body + description: ldap configuration. support input ldap service configuration. If it's a empty request, will load current configuration from the system. + required: false + schema: + $ref: '#/definitions/LdapConf' + tags: + - Products + responses: + 200: + description: Ping ldap service successfully. + 400: + description: Inviald ldap configuration parameters. + 401: + description: User need to login first. + 403: + description: Only admin has this authority. + 500: + description: Unexpected internal errors. + /ldap/users/search: + post: + summary: Search available ldap users. + description: | + This endpoint searches the available ldap users based on related configuration parameters. Support searched by input ladp configuration, load configuration from the system and specific filter. + parameters: + - name: username + in: query + type: string + required: false + description: Registered user ID + - name: ldap_conf + in: body + description: ldap search configuration. ldapconf field can input ldap service configuration. If this item are blank, will load default configuration will load current configuration from the system. + required: false + schema: + $ref: '#/definitions/LdapConf' + tags: + - Products + responses: + 200: + description: Search ldap users successfully. + schema: + type: array + items: + $ref: '#/definitions/LdapUsers' + 400: + description: Inviald ldap configuration parameters. + 401: + description: User need to login first. + 403: + description: Only admin has this authority. + 500: + description: Unexpected internal errors. + /ldap/users/import: + post: + summary: Import selected available ldap users. + description: | + This endpoint adds the selected available ldap users to harbor based on related configuration parameters from the system. System will try to guess the user email address and realname, add to harbor user information. + If have errors when import user, will return the list of importing failed uid and the failed reason. + parameters: + - name: uid_list + in: body + description: The uid listed for importing. This list will check users validity of ldap service based on configuration from the system. + required: true + schema: + $ref: '#/definitions/LdapImportUsers' + tags: + - Products + responses: + 200: + description: Add ldap users successfully. + 401: + description: User need to login first. + 403: + description: Only admin has this authority. + 500: + description: Failed import some users. + schema: + type: array + items: + $ref: '#/definitions/LdapFailedImportUsers' + /configurations: + get: + summary: Get system configurations. + description: | + This endpoint is for retrieving system configurations that only provides for admin user. + tags: + - Products + responses: + 200: + description: Get system configurations successfully. The response body is a map. + schema: + type: object + 401: + description: User need to log in first. + 403: + description: User does not have permission of admin role. + 500: + description: Unexpected internal errors. + put: + summary: Modify system configurations. + description: | + This endpoint is for modifying system configurations that only provides for admin user. + tags: + - Products + parameters: + - name: configurations + in: body + required: true + schema: + type: object + description: The configurations map need to be modified, the following are keys "auth_mode", "email_from", "email_host", "email_identity", "email_password", "email_port", "email_ssl", "email_username", "ldap_base_dn", "ldap_filter", "ldap_scope", "ldap_search_dn", "ldap_search_password", "ldap_timeout", "ldap_uid", "ldap_url", "project_creation_restriction", "self_registration", "verify_remote_cert". + responses: + 200: + description: Modify system configurations successfully. + 401: + description: User need to log in first. + 403: + description: User does not have permission of admin role. + 500: + description: Unexpected internal errors. + /configurations/reset: + post: + summary: Reset system configurations. + description: | + Reset system configurations from environment variables. Can only be accessed by admin user. + tags: + - Products + responses: + 200: + description: Reset system configurations successfully. + 401: + description: User need to log in first. + 403: + description: User does not have permission of admin role. + 500: + description: Unexpected internal errors. + /email/ping: + post: + summary: Test connection and authentication with email server. + description: | + Test connection and authentication with email server. + parameters: + - name: settings + in: body + description: Email server settings, if some of the settings are not assigned, they will be read from system configuration. + required: false + schema: + $ref: '#/definitions/EmailServerSetting' + tags: + - Products + responses: + 200: + description: Ping email server successfully. + 400: + description: Inviald email server settings. + 401: + description: User need to login first. + 403: + description: Only admin has this authority. + 500: + description: Unexpected internal errors. definitions: Search: type: object @@ -1318,26 +1627,12 @@ definitions: description: Search results of the projects that matched the filter keywords. type: array items: - $ref: '#/definitions/SearchProject' + $ref: '#/definitions/Project' repositories: description: Search results of the repositories that matched the filter keywords. type: array items: $ref: '#/definitions/SearchRepository' - SearchProject: - type: object - properties: - id: - type: integer - format: int64 - description: The ID of project - name: - type: string - description: The name of the project - public: - type: integer - format: int - description: The flag to indicate the publicity of the project (1 is public, 0 is non-public) SearchRepository: type: object properties: @@ -1353,6 +1648,12 @@ definitions: repository_name: type: string description: The name of the repository + pull_count: + type: integer + description: The count how many times the repository is pulled + tags_count: + type: integer + description: The count of tags in the repository ProjectReq: type: object properties: @@ -1403,7 +1704,7 @@ definitions: repo_count: type: integer description: The number of the repositories under this project. - Repository: + Manifest: type: object properties: manifest: @@ -1522,7 +1823,7 @@ definitions: TopRepo: type: object properties: - repo_name: + name: type: string description: The name of the repo count: @@ -1723,6 +2024,33 @@ definitions: password: type: string description: The target server password. + PingTarget: + type: object + properties: + endpoint: + type: string + description: The target address URL string. + username: + type: string + description: The target server username. + password: + type: string + description: The target server password. + PutTarget: + type: object + properties: + name: + type: string + description: The target name. + endpoint: + type: string + description: The target address URL string. + username: + type: string + description: The target server username. + password: + type: string + description: The target server password. HasAdminRole: type: object properties: @@ -1741,3 +2069,179 @@ definitions: comment: type: string description: The new comment. + Storage: + type: object + properties: + total: + type: integer + format: int64 + description: Total volume size. + free: + type: integer + format: int64 + description: Free volume size. + GeneralInfo: + type: object + properties: + with_notary: + type: boolean + description: If the Harbor instance is deployed with nested notary. + with_admiral: + type: boolean + description: If the Harbor instance is deployed with Admiral. + admiral_endpoint: + type: string + description: The url of the endpoint of admiral instance. + auth_mode: + type: string + description: The auth mode of current Harbor instance. + project_creation_restriction: + type: string + description: Indicate who can create projects, it could be 'adminonly' or 'everyone'. + self_registration: + type: boolean + description: Indicate whether the Harbor instance enable user to register himself. + has_ca_root: + type: boolean + description: Indicate whether there is a ca root cert file ready for download in the file system. + harbor_version: + type: string + description: The build version of Harbor. + SystemInfo: + type: object + properties: + storage: + type: array + description: The storage of system. + items: + $ref: '#/definitions/Storage' + LdapConf: + type: object + properties: + ldap_url: + type: string + description: The url of ldap service. + ldap_search_dn: + type: string + description: The search dn of ldap service. + ldap_search_password: + type: string + description: The search password of ldap service. + ldap_base_dn: + type: string + description: The base dn of ldap service. + ldap_filter: + type: string + description: The serach filter of ldap service. + ldap_uid: + type: string + description: The serach uid from ldap service attributes. + ldap_scope: + type: integer + format: int64 + description: The serach scope of ldap service. + ldap_connection_timeout: + type: integer + format: int64 + description: The connect timeout of ldap service(second). + LdapUsers: + type: object + properties: + ldap_username: + type: string + description: search ldap user name based on ldapconf. + ldap_realname: + type: string + description: system will try to guess the user realname form "uid" or "cn" attribute. + ldap_email: + type: string + description: system will try to guess the user email address form "mail" or "email" attribute. + LdapImportUsers: + type: object + properties: + ldap_uid_list: + type: array + description: selected uid list + items: + type: string + LdapFailedImportUsers: + type: object + properties: + ldap_uid: + type: string + description: the uid can't add to system. + error: + type: string + description: fail reason. + EmailServerSetting: + type: object + properties: + email_host: + type: string + description: The host of email server. + email_port: + type: integer + description: The port of email server. + email_username: + type: string + description: The username of email server. + email_password: + type: string + description: The password of email server. + email_ssl: + type: boolean + description: Use ssl/tls or not. + email_identity: + type: string + description: The dentity of email server. + RepoSignature: + type: object + properties: + tag: + type: string + description: The tag of image. + hashes: + type: object + description: The JSON object of the hash of the image. + DetailedTag: + type: object + properties: + tag: + type: string + description: The tag of image. + manifest: + type: object + description: The detail of manifest. + Repository: + type: object + properties: + id: + type: string + description: The ID of repository. + name: + type: string + description: The name of repository. + owner_id: + type: integer + description: The owner ID of repository. + project_id: + type: integer + description: The project ID of repository. + description: + type: string + description: The description of repository. + pull_count: + type: integer + description: The pull count of repository. + star_count: + type: integer + description: The star count of repository. + tags_count: + type: integer + description: The tags count of repository. + creation_time: + type: string + description: The creation time of repository. + update_time: + type: string + description: The update time of repository. diff --git a/docs/use_make.md b/docs/use_make.md new file mode 100644 index 000000000..ec2506540 --- /dev/null +++ b/docs/use_make.md @@ -0,0 +1,51 @@ +### Variables +Variable | Description +-------------------|------------- +BASEIMAGE | Container base image, default: photon +DEVFLAG | Build model flag, default: dev +COMPILETAG | Compile model flag, default: compile_normal (local golang build) +GOBUILDIMAGE | Golang image to compile harbor go source code. +CLARITYIMAGE | Clarity image that based on Node to compile UI. +NOTARYFLAG | Whether to enable notary in harbor, default:false +HTTPPROXY | Clarity proxy to build UI. + + +### Targets +Target | Description +--------------------|------------- +all | prepare env, compile binaries, build images and install images +prepare | prepare env +compile | compile ui and jobservice code +compile_ui | compile ui binary +compile_jobservice | compile jobservice binary +compile_clarity | compile clarity ui binary +compile_adminserver | compile admin server binary +build | build Harbor docker images (default: using build_photon) +build_photon | build Harbor docker images from Photon OS base image +install | compile binaries, build images, prepare specific version of compose file and startup Harbor instance +start | startup Harbor instance +down | shutdown Harbor instance +package_online | prepare online install package +package_offline | prepare offline install package +pushimage | push Harbor images to specific registry server +clean all | remove binary, Harbor images, specific version docker-compose file, specific version tag and online/offline install package +cleanbinary | remove ui and jobservice binary +cleanimage | remove Harbor images +cleandockercomposefile | remove specific version docker-compose +cleanversiontag | remove specific version tag +cleanpackage | remove online/offline install package +version | set harbor version + +#### EXAMPLE: + +#### Build and run harbor from source code. +make install GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:0.8.4 NOTARYFLAG=true HTTPPROXY= + +### Package offline installer +make package_offline GOBUILDIMAGE=golang:1.7.3 COMPILETAG=compile_golangimage CLARITYIMAGE=vmware/harbor-clarity-ui-builder:0.8.4 NOTARYFLAG=true HTTPPROXY= + +### Start harbor with notary +make -e NOTARYFLAG=true start + +### Stop harbor with notary +make -e NOTARYFLAG=true down diff --git a/docs/use_notary.md b/docs/use_notary.md new file mode 100644 index 000000000..fa585f1eb --- /dev/null +++ b/docs/use_notary.md @@ -0,0 +1,22 @@ +### Setup +In harbor.cfg, make sure the attribute ```ui_url_protocol``` is set to ```https```, and the attributes ```ssl_cert``` and ```ssl_cert_key``` are pointed to valid certificates. For more information about generating https certificate please refer to: [Configuring HTTPS for Harbor](configure_https.md) + +### Copy Root Certificate +Suppose the Harbor instance is hosted on a machine ```192.168.0.5``` +If you are using a self-signed cetificate, make sure to copy the CA root cert to ```/etc/docker/certs.d/192.168.0.5/``` and ```~/.docker/tls/192.168.0.5:4443/``` + +### Enable Docker Content Trust +It can be done via setting envrironment variables: + +``` +export DOCKER_CONTENT_TRUST=1 +export DOCKER_CONTENT_TRUST_SERVER=https://192.168.0.5:4443 +``` + +### Set alias for notary (optional) +Because by default the local directory for storing meta files for notary client is different from docker client. If you want to use notary client to manipulate the keys/meta files generated by Docker Content Trust, please set the alias to reduce the effort: + +``` +alias notary="notary -s https//192.168.0.5:4443 -d ~/.docker/trust --tlscacert /etc/docker/certs.d/192.168.0.5/ca.crt" + +``` diff --git a/docs/user_guide.md b/docs/user_guide.md index 168fb4cec..e45bca95e 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -1,5 +1,5 @@ -#User Guide -##Overview +# User Guide +## Overview This guide walks you through the fundamentals of using Harbor. You'll learn how to use Harbor to: * Manage your projects. @@ -7,14 +7,15 @@ This guide walks you through the fundamentals of using Harbor. You'll learn how * Replicate projects to a remote registry. * Search projects and repositories. * Manage Harbor system if you are the system administrator: - + Manage users. - + Manage destinations. - + Manage replication policies. + * Manage users. + * Manage destinations. + * Manage replication policies. + * Manage configuration. * Pull and push images using Docker client. * Delete repositories and images. +* Content trust. - -##Role Based Access Control +## Role Based Access Control(RBAC) ![rbac](img/rbac.png) @@ -27,16 +28,16 @@ Harbor manages images through projects. Users can be added into one project as a Besides the above three roles, there are two system-wide roles: * **SysAdmin**: "SysAdmin" has the most privileges. In addition to the privileges mentioned above, "SysAdmin" can also list all projects, set an ordinary user as administrator and delete users. The public project "library" is also owned by the administrator. -* **Anonymous**: When a user is not logged in, the user is considered as an "anonymous" user. An anonymous user has no access to private projects and has read-only access to public projects. +* **Anonymous**: When a user is not logged in, the user is considered as an "Anonymous" user. An anonymous user has no access to private projects and has read-only access to public projects. -##User account +## User account Harbor supports two authentication modes: * **Database(db_auth)** Users are stored in the local database. - A user can self register himself/herself in Harbor in this mode. To disable user self-registration, refer to the [installation guide](installation_guide_ova.md). When self-registration is disabled, the system administrator can add users in Harbor. + A user can register himself/herself in Harbor in this mode. To disable user self-registration, refer to the [installation guide](installation_guide.md) for initial configuration, or disable this feature in [Administrator Options](#administrator-options). When self-registration is disabled, the system administrator can add users into Harbor. When registering or adding a new user, the username and email must be unique in the Harbor system. The password must contain at least 8 characters with 1 lowercase letter, 1 uppercase letter and 1 numeric character. @@ -51,11 +52,11 @@ Harbor supports two authentication modes: Under this authentication mode, users whose credentials are stored in an external LDAP or AD server can log in to Harbor directly. - When an LDAP/AD user logs in by *username* and *password*, Harbor binds to the LDAP/AD server with the **"LDAP Search DN"** and **"LDAP Search Password"** described in [installation guide](installation_guide_ova.md). If it successes, Harbor looks up the user under the LDAP entry **"LDAP Base DN"** including substree. The attribute (such as uid, cn) specified by **"LDAP UID"** is used to match a user with the *username*. If a match is found, the user's *password* is verified by a bind request to the LDAP/AD server. + When an LDAP/AD user logs in by *username* and *password*, Harbor binds to the LDAP/AD server with the **"LDAP Search DN"** and **"LDAP Search Password"** described in [installation guide](installation_guide.md). If it succeeded, Harbor looks up the user under the LDAP entry **"LDAP Base DN"** including substree. The attribute (such as uid, cn) specified by **"LDAP UID"** is used to match a user with the *username*. If a match is found, the user's *password* is verified by a bind request to the LDAP/AD server. - Self-registration, changing password and resetting password are not supported anymore under LDAP/AD authentication mode because the users are managed by LDAP or AD. + Self-registration, changing password and resetting password are not supported under LDAP/AD authentication mode because the users are managed by LDAP or AD. -##Managing projects +## Managing projects A project in Harbor contains all repositories of an application. No images can be pushed to Harbor before the project is created. RBAC is applied to a project. There are two types of projects in Harbor: * **Public**: All users have the read privilege to a public project, it's convenient for you to share some repositories with others in this way. @@ -73,70 +74,96 @@ All logs can be listed by clicking "Logs". You can apply a filter by username, o ![browse project](img/new_project_log.png) -##Managing members of a project -###Adding members +## Managing members of a project +### Adding members You can add members with different roles to an existing project. ![browse project](img/new_add_member.png) -###Updating and removing members -You can update or remove a member by clicking the icon on the right. +### Updating and removing members +You can update or remove a member by clicking the icon on the left. ![browse project](img/new_remove_update_member.png) -##Replicating images +## Replicating images Images replication is used to replicate repositories from one Harbor instance to another. -The function is project-oriented, and once the system administrator set a policy to one project, all repositories under the project will be replicated to the remote registry. Each repository will start a job to run. If the project does not exist on the remote registry, a new project will be created automatically, but if it already exists and the user configured in policy has no write privilege to it, the process will fail. When a new repository is pushed to this project or an existing repository is deleted from this project, the same operation will also be replicated to the destination. The member information will not be replicated. +The function is project-oriented, and once the system administrator set a rule to one project, all repositories under the project will be replicated to the remote registry. Each repository will start a job to run. If the project does not exist on the remote registry, a new project will be created automatically, but if it already exists and the user configured in policy has no write privilege to it, the process will fail. When a new repository is pushed to this project or an existing repository is deleted from this project, the same operation will also be replicated to the destination. The member information will not be replicated. There may be a bit of delay during replication according to the situation of the network. If replication job fails due to the network issue, the job will be re-scheduled a few minutes later. **Note:** The replication feature is incompatible between Harbor instance before version 0.3.5(included) and after version 0.3.5. -Start replication by creating a policy. Click "Add New Policy" on the "Replication" tab, fill the necessary fields, if there is no destination in the list, you need to create one, and then click "OK", a policy for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately. +Start replication by creating a rule. Click "Add Replication Rule" on the "Replication" tab, fill in the necessary fields, if there is no endpoint in the list, you need to create one, and then click "OK", a rule for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately. -![browse project](img/new_create_policy.png) +![browse project](img/new_create_rule.png) -You can enable, disable or delete a policy in the policy list view. Only policies which are disabled can be edited and only policies which are disabled and have no running jobs can be deleted. If a policy is disabled, the running jobs under it will be stopped. +You can enable, disable or delete a rule in the rule list view. Only rules which are disabled can be edited and only rules which are disabled and have no running jobs can be deleted. If a rule is disabled, the running jobs under it will be stopped. -Click a policy, jobs which belong to this policy will be listed. A job represents the progress which will replicate a repository of one project to the remote. +Click a rule, jobs which belong to this rule will be listed. A job represents the progress of replicating the repository to the remote instance. -![browse project](img/new_policy_list.png) +![browse project](img/new_rule_list.png) -##Searching projects and repositories -Entering a keyword in the search field at the top lists all matching projects and repositories. The search result includes both public and private repositories you have access privilege to. +## Searching projects and repositories +Entering a keyword in the search field at the top lists all matching projects and repositories. The search result includes both public and private repositories you have access to. ![browse project](img/new_search.png) -##Administrator options -###Managing user -Administrator can add "administrator" role to an ordinary user by toggling the switch under "Administrator". To delete a user, click on the recycle bin icon. +## Administrator options +### Managing user +Administrator can add "Administrator" role to an ordinary user by click button on the left and select "Set as Administrator". To delete a user, select "Delete". ![browse project](img/new_set_admin_remove_user.png) -###Managing destination -You can list, add, edit and delete destinations in the "Destination" tab. Only destinations which are not referenced by any policies can be edited. +### Managing endpoint +You can list, add, edit and delete endpoints in the "Endpoints" tab. Only endpoints which are not referenced by any enabled rules can be edited. -![browse project](img/new_manage_destination.png) +![browse project](img/new_manage_endpoint.png) -###Managing replication -You can list, edit, enable and disable policies in the "Replication" tab. Make sure the policy is disabled before you edit it. +### Managing replication +You can list, edit, enable and disable rules in the "Replication" tab. Make sure the policy is disabled before you edit it. ![browse project](img/new_manage_replication.png) -##Pulling and pushing images using Docker client +### Managing authentication +You can change authentication mode between **Database**(default) and **LDAP** before any user is added, when there is at least one user(besides admin) in Harbor, you cannot change the authentication mode. +![browse project](img/new_auth.png) +When using LDAP mode, user's self-registration is disabled. The parameters of LDAP server must be filled in. For more information, refer to [User account](#user-account). +![browse project](img/ldap_auth.png) + +### Managing project creation +Use the **Project Creation** drop-down menu to set which users can create projects. Select **Everyone** to allow all users to create projects. Select **Admin Only** to allow only users with the Administrator role to create projects. +![browse project](img/new_proj_create.png) + +### Managing self-registration +You can manage whether a user can sign up for a new account. This option is not available if you use LDAP authentication. +![browse project](img/new_self_reg.png) + +### Managing verification of remote certificate +You can choose whether to verify remote endpoint's certification. You may need to disable certificate verification if the remote registry uses a self-signed or an untrusted certificate. +![browse project](img/new_remote_cert.png) + +### Managing email settings +You can change Harbor's email settings, the mail server is used to send out responses to users who request to reset their password. +![browse project](img/new_config_email.png) + +## Pulling and pushing images using Docker client **NOTE: Harbor only supports Registry V2 API. You need to use Docker client 1.6.0 or higher.** -Harbor supports HTTP by default and Docker client tries to connect to Harbor using HTTPS first, so if you encounter an error as below when you pull or push images, you need to add '--insecure-registry' option to /etc/default/docker (ubuntu) or /etc/sysconfig/docker (centos) and restart Docker: -*FATA[0000] Error response from daemon: v1 ping attempt failed with error: -Get https://myregistrydomain.com:5000/v1/_ping: tls: oversized record received with length 20527. -If this private registry supports only HTTP or HTTPS with an unknown CA certificate,please add -`--insecure-registry myregistrydomain.com:5000` to the daemon's arguments. -In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; -simply place the CA certificate at /etc/docker/certs.d/myregistrydomain.com:5000/ca.crt* +Harbor supports HTTP by default and Docker client tries to connect to Harbor using HTTPS first, so if you encounter an error as below when you pull or push images, you need to add '--insecure-registry' option to ```/etc/default/docker``` (ubuntu) or ```/etc/sysconfig/docker``` (centos) and restart Docker: + -###Pulling images +```Error response from daemon: Get https://myregistrydomain.com/v1/users/: dial tcp myregistrydomain.com:443 getsockopt: connection refused.``` + + +If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add +`--insecure-registry myregistrydomain.com` to the daemon's start up arguments. + + +In the case of HTTPS, if you have access to the registry's CA certificate, simply place the CA certificate at /etc/docker/certs.d/myregistrydomain.com/ca.crt . + +### Pulling images If the project that the image belongs to is private, you should sign in first: ```sh @@ -149,9 +176,9 @@ You can now pull the image: $ docker pull 10.117.169.182/library/ubuntu:14.04 ``` -**Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node.** +**Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node. You cannot pull a unsigned image if you enabled content trust.** -###Pushing images +### Pushing images Before pushing an image, you must create a corresponding project on Harbor web UI. First, log in from Docker client: @@ -174,16 +201,17 @@ $ docker push 10.117.169.182/demo/ubuntu:14.04 **Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node.** -##Deleting repositories +### Deleting repositories Repository deletion runs in two steps. First, delete a repository in Harbor's UI. This is soft deletion. You can delete the entire repository or just a tag of it. After the soft deletion, the repository is no longer managed in Harbor, however, the files of the repository still remain in Harbor's storage. -![browse project](img/new_delete_repository.png) +![browse project](img/new_delete_repo.png) +![browse project](img/new_delete_tag.png) -**CAUTION: If both tag A and tag B refer to the same image, after deleting tag A, B will also get deleted.** +**CAUTION: If both tag A and tag B refer to the same image, after deleting tag A, B will also get deleted. if you enabled content trust, you need to use notary command line tool to delete the tag's signature before you delete an image.** Next, delete the actual files of the repository using the registry's garbage collection(GC). Make sure that no one is pushing images or Harbor is not running at all before you perform a GC. If someone were pushing an image while GC is running, there is a risk that the image's layers will be mistakenly deleted which results in a corrupted image. So before running GC, a preferred approach is to stop Harbor first. @@ -191,15 +219,32 @@ Run the below commands on the host which Harbor is deployed on to preview what f ```sh $ docker-compose stop -$ docker run -it --name gc --rm --volumes-from registry registry:2.5.0 garbage-collect --dry-run /etc/registry/config.yml + +$ docker run -it --name gc --rm --volumes-from registry vmware/registry:photon-2.6.0 garbage-collect --dry-run /etc/registry/config.yml + ``` **NOTE:** The above option "--dry-run" will print the progress without removing any data. Verify the result of the above test, then use the below commands to perform garbage collection and restart Harbor. ```sh -$ docker run -it --name gc --rm --volumes-from registry registry:2.5.0 garbage-collect /etc/registry/config.yml + +$ docker run -it --name gc --rm --volumes-from registry vmware/registry:photon-2.6.0 garbage-collect /etc/registry/config.yml + $ docker-compose start ``` For more information about GC, please see [GC](https://github.com/docker/docker.github.io/blob/master/registry/garbage-collection.md). + +### Content trust +If you want to enable content trust to ensure that images are signed, please set two environment variables in the command line before pushing or pulling any image: +```sh +export DOCKER_CONTENT_TRUST=1 +export DOCKER_CONTENT_TRUST_SERVER=https://10.117.169.182:4443 +``` +If you are using a self-signed cert, make sure to copy the CA cert into ```/etc/docker/certs.d/10.117.169.182``` and ```$HOME/.docker/tls/10.117.169.182:4443/```. When an image is signed, it is indicated in the Web UI. +**Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node. In order to use content trust, HTTPS must be enabled in Harbor.** + + +When an image is signed, it has a tick shown in UI; otherwise, a cross sign(X) is displayed instead. +![browse project](img/content_trust.png) diff --git a/docs/user_guide_ova.md b/docs/user_guide_ova.md deleted file mode 100644 index 70a4dcab1..000000000 --- a/docs/user_guide_ova.md +++ /dev/null @@ -1,207 +0,0 @@ -#User Guide of Harbor Virtual Appliance -##Overview -This guide walks you through the fundamentals of using Harbor virtual appliance. You'll learn how to use Harbor to: - -* Manage your projects. -* Manage members of a project. -* Replicate projects to a remote registry. -* Search projects and repositories. -* Manage Harbor system if you are the system administrator: - + Manage users. - + Manage destinations. - + Manage replication policies. -* Pull and push images using Docker client. -* Delete repositories and images. - - -##Role Based Access Control - -![rbac](img/rbac.png) - -In Harbor, images are grouped under projects. To access an image, a user should be added as a member into the project of the image. A member can have one of the three roles: - -* **Guest**: Guest has read-only privilege for a specified project. -* **Developer**: Developer has read and write privileges for a project. -* **ProjectAdmin**: When creating a new project, you will be assigned the "ProjectAdmin" role to the project. Besides read-write privileges, the "ProjectAdmin" also has some management privileges, such as adding and removing members. - -Besides the above three roles, there are two system-wide roles: - -* **SysAdmin**: "SysAdmin" has the most privileges. In addition to the privileges mentioned above, "SysAdmin" can also list all projects, set an ordinary user as administrator and delete users. The public project "library" is also owned by the administrator. -* **Anonymous**: When a user is not logged in, the user is considered as an "anonymous" user. An anonymous user has no access to private projects and has read-only access to public projects. - -##User account -Harbor supports two authentication modes: - -* **Database(db_auth)** - - Users are stored in the local database. - - A user can register himself/herself in Harbor in this mode. To disable user self-registration, refer to the **[installation guide](installation_guide_ova.md)**. When self-registration is disabled, the system administrator can add users in Harbor. - - When registering or adding a new user, the username and email must be unique in the Harbor system. The password must contain at least 8 characters, less than 20 characters with 1 lowercase letter, 1 uppercase letter and 1 numeric character. - - When you forgot your password, you can follow the below steps to reset the password: - - 1. Click the link "Forgot Password" in the sign in page. - 2. Input the email address entered when you signed up, an email will be sent out to you for password reset. - 3. After receiving the email, click on the link in the email which directs you to a password reset web page. - 4. Input your new password and click "Save". - -* **LDAP/Active Directory (ldap_auth)** - - Under this authentication mode, users whose credentials are stored in an external LDAP or AD server can log in to Harbor directly. - - When an LDAP/AD user logs in by *username* and *password*, Harbor binds to the LDAP/AD server with the **"LDAP Search DN"** and **"LDAP Search Password"** described in [installation guide](installation_guide_ova.md). If it successes, Harbor looks up the user under the LDAP entry **"LDAP Base DN"** including substree. The attribute (such as uid, cn) specified by **"LDAP UID"** is used to match a user with the *username*. If a match is found, the user's *password* is verified by a bind request to the LDAP/AD server. - - Self-registration, changing password and resetting password are not supported anymore under LDAP/AD authentication mode because the users are managed by LDAP or AD. - -##Managing projects -A project in Harbor contains all repositories of an application. No images can be pushed to Harbor before the project is created. RBAC is applied to a project. There are two types of projects in Harbor: - -* **Public**: All users have the read privilege to a public project, it's convenient for you to share some repositories with others in this way. -* **Private**: A private project can only be accessed by users with proper privileges. - -You can create a project after you signed in. Enabling the "Public" checkbox makes the project public. - -![create project](img/new_create_project.png) - -After the project is created, you can browse repositories, users and logs using the navigation tab. - -![browse project](img/new_browse_project.png) - -All logs can be listed by clicking "Logs". You can apply a filter by username, or operations and dates under "Advanced Search". - -![browse project](img/new_project_log.png) - -##Managing members of a project -###Adding members -You can add members with different roles to an existing project. - -![browse project](img/new_add_member.png) - -###Updating and removing members -You can update or remove a member by clicking the icon on the right. - -![browse project](img/new_remove_update_member.png) - -##Replicating images -Images can be replicated between Harbor instances. It can be used to transfer images from one data center to another, or from an on-prem registry to an instance in the cloud. - -A replication policy needs to be set up on the source instance to govern the replication process. -One key fact about the replication is that only images are replicated between Harbor instances. -Users, roles and other information are not replicated. As such, always keep in mind that the user, roles and policy information is individually managed by each Harbor instance. - -The replication is project-based. When a system administrator sets a policy to a project, all repositories under the project will be replicated to the remote registry. A replication job will be scheduled for each repository. -If the project does not exist on the remote registry, a new project is created automatically. -If the project already exists and the replication user configured in the policy has no write privilege to it, -the process will fail. - -When the policy is first enabled, all images of the project are replicated to the remote registry. Images subsequently pushed to the project on the source registry -will be incrementally replicated to the remote instance. When an image is deleted from the source registry, the policy ensures that the remote registry deletes the same image as well. -Please note, the user and member information will not be replicated. - -Depending on the size of the images and the network condition, the replication requires some time to complete. On the remote registry, an image is not available until -all its layers have been synchronized from the source. If a replication job fails due to some network issue, the job will be scheduled for a retry after a few minutes. -Always checks the log to see if there is any error of the replication. When a policy is disabled (stopped), Harbor tries to stop all existing jobs. It may take a while -before all jobs finish. A policy can be restarted by disabling and then enabling it again. - -To enable image replication, a policy must first be created. Click "Add New Policy" on the "Replication" tab, fill the necessary fields, if there is no destination in the list, you need to create one, and then click "OK", a policy for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately. - -**Note:** Set **"Verify Remote Cert"** to off according to the [installation guide](installation_guide_ova.md) if the destination uses a self-signed or untrusted certificate. - -![browse project](img/new_create_policy.png) - -You can enable, disable or delete a policy in the policy list view. Only policies which are disabled can be edited. Only policies which are disabled and have no running jobs can be deleted. If a policy is disabled, the running jobs under it will be stopped. - -Click on a policy, jobs belonging to this policy will be listed. A job represents the progress of replicating a repository to the remote instance. - -![browse project](img/new_policy_list.png) - -##Searching projects and repositories -Entering a keyword in the search field at the top lists all matching projects and repositories. The search result includes both public and private repositories you have access privilege to. - -![browse project](img/new_search.png) - -##Administrator options -###Managing user -Administrator can add "administrator" role to an ordinary user by toggling the switch under "Administrator". To delete a user, click on the recycle bin icon. - -![browse project](img/new_set_admin_remove_user.png) - -###Managing destination -You can list, add, edit and delete destinations in the "Destination" tab. Only destinations which are not referenced by any policies can be edited. - -![browse project](img/new_manage_destination.png) - -###Managing replication -You can list, edit, enable and disable policies in the "Replication" tab. Make sure the policy is disabled before you edit it. - -![browse project](img/new_manage_replication.png) - -##Pulling and pushing images using Docker client - -**NOTE: Harbor only supports Registry V2 API. You need to use Docker client 1.6.0 or higher.** - -Harbor uses HTTPS for secure communication by default. A self-signed certificate is generated at first boot based on its FQDN (Fully Qualified Domain Name) or IP address. If you use Docker client to interact with it, there are two options you can choose: - -1. Trust the certificate of Harbor's CA -Refer to the "Getting Certificate of Harbor's CA" part of [installation guide](installation_guide_ova.md). -2. Set "--insecure-registry" option -Add "--insecure-registry" option to /etc/default/docker (ubuntu) or /etc/sysconfig/docker (centos) and restart Docker service. - -If Harbor is configured as using HTTP, just set the "--insecure-registry" option. - -If the certificate used by Harbor is signed by a trusted authority, Docker should work without any additional configuration. - -###Pulling images -If the project that the image belongs to is private, you should sign in first: - -```sh -$ docker login 10.117.169.182 -``` - -You can now pull the image: - -```sh -$ docker pull 10.117.169.182/library/ubuntu:14.04 -``` - -**Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node.** - -###Pushing images -Before pushing an image, you must create a corresponding project on Harbor web UI. - -First, log in from Docker client: - -```sh -$ docker login 10.117.169.182 -``` - -Tag the image: - -```sh -$ docker tag ubuntu:14.04 10.117.169.182/demo/ubuntu:14.04 -``` - -Push the image: - -```sh -$ docker push 10.117.169.182/demo/ubuntu:14.04 -``` - -**Note: Replace "10.117.169.182" with the IP address or domain name of your Harbor node.** - -##Deleting repositories - -Repository deletion runs in two steps. - -First, delete a repository in Harbor's UI. This is soft deletion. You can delete the entire repository or just a tag of it. After the soft deletion, -the repository is no longer managed in Harbor, however, the files of the repository still remain in Harbor's storage. - -![browse project](img/new_delete_repository.png) - -**CAUTION: If both tag A and tag B refer to the same image, after deleting tag A, B will also get deleted.** - -Next, set **"Garbage Collection"** to true according to the [installation guide](installation_guide_ova.md)(skip this step if this flag has already been set) and reboot the VM, Harbor will perform garbage collection when it boots up. - -For more information about garbage collection, please see Docker's document on [GC](https://github.com/docker/docker.github.io/blob/master/registry/garbage-collection.md). diff --git a/make/common/db/Dockerfile b/make/common/db/Dockerfile index c8555675a..ff1a3e850 100644 --- a/make/common/db/Dockerfile +++ b/make/common/db/Dockerfile @@ -1,4 +1,4 @@ -FROM mysql:5.6 +FROM mysql:5.6.35 WORKDIR /tmp diff --git a/make/common/db/docker-entrypoint.sh b/make/common/db/docker-entrypoint.sh index 95c46e939..32172eeb6 100644 --- a/make/common/db/docker-entrypoint.sh +++ b/make/common/db/docker-entrypoint.sh @@ -13,6 +13,7 @@ if [ ! -d '/var/lib/mysql/mysql' -a "${1%_safe}" = 'mysqld' ]; then # These statements _must_ be on individual lines, and _must_ end with # semicolons (no line breaks or comments are permitted). # TODO proper SQL escaping on ALL the things D: + printf -v MYSQL_ROOT_PASSWORD "%q" ${MYSQL_ROOT_PASSWORD} TEMP_FILE='/tmp/mysql-first-time.sql' cat > "$TEMP_FILE" <<-EOSQL DELETE FROM mysql.user ; diff --git a/make/common/templates/adminserver/env b/make/common/templates/adminserver/env new file mode 100644 index 000000000..190acac5a --- /dev/null +++ b/make/common/templates/adminserver/env @@ -0,0 +1,39 @@ +LOG_LEVEL=debug +EXT_ENDPOINT=$ui_url +AUTH_MODE=$auth_mode +SELF_REGISTRATION=$self_registration +LDAP_URL=$ldap_url +LDAP_SEARCH_DN=$ldap_searchdn +LDAP_SEARCH_PWD=$ldap_search_pwd +LDAP_BASE_DN=$ldap_basedn +LDAP_FILTER=$ldap_filter +LDAP_UID=$ldap_uid +LDAP_SCOPE=$ldap_scope +LDAP_TIMEOUT=$ldap_timeout +DATABASE_TYPE=mysql +MYSQL_HOST=mysql +MYSQL_PORT=3306 +MYSQL_USR=root +MYSQL_PWD=$db_password +MYSQL_DATABASE=registry +REGISTRY_URL=http://registry:5000 +TOKEN_SERVICE_URL=http://ui/service/token +EMAIL_HOST=$email_host +EMAIL_PORT=$email_port +EMAIL_USR=$email_usr +EMAIL_PWD=$email_pwd +EMAIL_SSL=$email_ssl +EMAIL_FROM=$email_from +EMAIL_IDENTITY=$email_identity +HARBOR_ADMIN_PASSWORD=$harbor_admin_password +PROJECT_CREATION_RESTRICTION=$project_creation_restriction +VERIFY_REMOTE_CERT=$verify_remote_cert +MAX_JOB_WORKERS=$max_job_workers +UI_SECRET=$ui_secret +JOBSERVICE_SECRET=$jobservice_secret +TOKEN_EXPIRATION=$token_expiration +CFG_EXPIRATION=5 +GODEBUG=netdns=cgo +ADMIRAL_URL=$admiral_url +WITH_NOTARY=$with_notary +RESET=false diff --git a/make/common/templates/jobservice/env b/make/common/templates/jobservice/env index c6e9fd736..c5e37fc0f 100644 --- a/make/common/templates/jobservice/env +++ b/make/common/templates/jobservice/env @@ -1,15 +1,5 @@ -MYSQL_HOST=mysql -MYSQL_PORT=3306 -MYSQL_USR=root -MYSQL_PWD=$db_password -UI_SECRET=$ui_secret -SECRET_KEY=$secret_key -CONFIG_PATH=/etc/jobservice/app.conf -REGISTRY_URL=http://registry:5000 -VERIFY_REMOTE_CERT=$verify_remote_cert -MAX_JOB_WORKERS=$max_job_workers LOG_LEVEL=debug -LOG_DIR=/var/log/jobs +CONFIG_PATH=/etc/jobservice/app.conf +UI_SECRET=$ui_secret +JOBSERVICE_SECRET=$jobservice_secret GODEBUG=netdns=cgo -EXT_ENDPOINT=$ui_url -TOKEN_ENDPOINT=http://ui diff --git a/make/common/templates/nginx/nginx.http.conf b/make/common/templates/nginx/nginx.http.conf index 980d9ece2..b776f7436 100644 --- a/make/common/templates/nginx/nginx.http.conf +++ b/make/common/templates/nginx/nginx.http.conf @@ -21,6 +21,12 @@ http { server ui:80; } + log_format timed_combined '$$remote_addr - ' + '"$$request" $$status $$body_bytes_sent ' + '"$$http_referer" "$$http_user_agent" ' + '$$request_time $$upstream_response_time $$pipe'; + + access_log /dev/stdout timed_combined; server { listen 80; diff --git a/make/common/templates/nginx/nginx.https.conf b/make/common/templates/nginx/nginx.https.conf index 05ede9a5d..131aec466 100644 --- a/make/common/templates/nginx/nginx.https.conf +++ b/make/common/templates/nginx/nginx.https.conf @@ -8,11 +8,11 @@ events { http { tcp_nodelay on; + include /etc/nginx/conf.d/*.upstream.conf; # this is necessary for us to be able to disable request buffering in all cases proxy_http_version 1.1; - upstream registry { server registry:5000; } @@ -20,7 +20,15 @@ http { upstream ui { server ui:80; } + + log_format timed_combined '$$remote_addr - ' + '"$$request" $$status $$body_bytes_sent ' + '"$$http_referer" "$$http_user_agent" ' + '$$request_time $$upstream_response_time $$pipe'; + access_log /dev/stdout timed_combined; + + include /etc/nginx/conf.d/*.server.conf; server { listen 443 ssl; diff --git a/make/common/templates/nginx/notary.server.conf b/make/common/templates/nginx/notary.server.conf new file mode 100644 index 000000000..566ca9f6f --- /dev/null +++ b/make/common/templates/nginx/notary.server.conf @@ -0,0 +1,33 @@ + server { + listen 4443 ssl; + + # ssl + ssl_certificate $ssl_cert; + ssl_certificate_key $ssl_cert_key; + + # recommendations from https://raymii.org/s/tutorials/strong_ssl_security_on_nginx.html + ssl_protocols tlsv1.1 tlsv1.2; + ssl_ciphers '!aNULL:kECDH+AESGCM:ECDH+AESGCM:RSA+AESGCM:kECDH+AES:ECDH+AES:RSA+AES:'; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:ssl:10m; + + # disable any limits to avoid http 413 for large image uploads + client_max_body_size 0; + + # required to avoid http 411: see issue #1486 (https://github.com/docker/docker/issues/1486) + chunked_transfer_encoding on; + + location /v2/ { + proxy_pass http://notary-server/v2/; + proxy_set_header Host $$http_host; + proxy_set_header X-Real-IP $$remote_addr; + proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for; + + # When setting up Harbor behind other proxy, such as an Nginx instance, remove the below line if the proxy already has similar settings. + proxy_set_header X-Forwarded-Proto $$scheme; + + proxy_buffering off; + proxy_request_buffering off; + } + } + diff --git a/make/common/templates/nginx/notary.upstream.conf b/make/common/templates/nginx/notary.upstream.conf new file mode 100644 index 000000000..ba4d60d4c --- /dev/null +++ b/make/common/templates/nginx/notary.upstream.conf @@ -0,0 +1,4 @@ + + upstream notary-server { + server notary-server:4443; + } diff --git a/make/common/templates/notary/mysql-initdb.d/initial-notaryserver.sql b/make/common/templates/notary/mysql-initdb.d/initial-notaryserver.sql new file mode 100644 index 000000000..ae124ed15 --- /dev/null +++ b/make/common/templates/notary/mysql-initdb.d/initial-notaryserver.sql @@ -0,0 +1,7 @@ +CREATE DATABASE IF NOT EXISTS `notaryserver`; + +CREATE USER "server"@"notary-server.%" IDENTIFIED BY ""; + +GRANT + ALL PRIVILEGES ON `notaryserver`.* + TO "server"@"notary-server.%" diff --git a/make/common/templates/notary/mysql-initdb.d/initial-notarysigner.sql b/make/common/templates/notary/mysql-initdb.d/initial-notarysigner.sql new file mode 100644 index 000000000..a3917d508 --- /dev/null +++ b/make/common/templates/notary/mysql-initdb.d/initial-notarysigner.sql @@ -0,0 +1,7 @@ +CREATE DATABASE IF NOT EXISTS `notarysigner`; + +CREATE USER "signer"@"notary-signer.%" IDENTIFIED BY ""; + +GRANT + ALL PRIVILEGES ON `notarysigner`.* + TO "signer"@"notary-signer.%"; diff --git a/make/common/templates/notary/notary-signer-ca.crt b/make/common/templates/notary/notary-signer-ca.crt new file mode 100644 index 000000000..c30df3cbf --- /dev/null +++ b/make/common/templates/notary/notary-signer-ca.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFhjCCA26gAwIBAgIJALJdsE+BUxypMA0GCSqGSIb3DQEBCwUAMF8xCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEPMA0G +A1UECgwGRG9ja2VyMRowGAYDVQQDDBFOb3RhcnkgVGVzdGluZyBDQTAeFw0xNzAx +MjMwNjAzMzZaFw0yNzAxMjEwNjAzMzZaMF8xCzAJBgNVBAYTAlVTMQswCQYDVQQI +DAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEPMA0GA1UECgwGRG9ja2VyMRow +GAYDVQQDDBFOb3RhcnkgVGVzdGluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALIZNBcIoQDJql5w+XULXq9W3tmD47xnf+IG4u7hkDVPCT4xRG74 +LBoSuFyPUrfT+tsibMlNG6XRtSfLQdNNeQuyIuiilNXV0kXB0RR3TrhxCaKdhRU5 +oQGfpYMvbPNFB7WU/5aAiQutHH85hEMPECf1qPjq8YlUaXJLGFY3WRkW+OOBZ78U +00PqKlvC1kR/NbsV3IkMrO+vWWJQrPFusyYjQ511eQXnRtt8P0Qic0azPffQDVxC +WUe47hmdQ1AULbxQ9AZcPlMI7UFqo+/w/4hPEGJMeOWirLvHLXg4nsOwy7DfWl/n +MqLdJOC/KNfQVAQtkteeZZkkIIV1gxTPYsJqPNwkP9GdJK1A8NW1ef75v7xbQCPY +03QQonBEK7ny7b1xXGGgJzXvK9RP0UUwjt/815c4d0cgUHsy4yuvl2F44EObRshk +fjJVsN/0wrtq4QLE5ZvbeO+7to8dLcRxkmB8axhxahega7akUyY0WxZ+iSn6fzft +/xeCcs/L10V5z0kK4PbiNnooDzV4B6Dy/5oyNExw0jgpD0mzOK5aLb0tXGqFT/ZJ +9vydelBq5q4jLV7SHhHM1dBJSv1fl7vOpDlEr7LBd4YAO2BowoyGLHtLhgYybXF+ +CZ9ywPb1dIIcdK5IVeZECNHMSBuhCRZUu+aun8tRcdSgLEX7mQ/GKWELAgMBAAGj +RTBDMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgFGMB0GA1UdDgQW +BBSWWbcCebeEgZlWk2/k+abh+bEFpDANBgkqhkiG9w0BAQsFAAOCAgEAQ9gA3Q4b +r2+ZJdIDoDzCNdtHQbb/d1NiUP/Na1MFo7omR3MnKGXy3dIp9IrQq6ROhlqUhDvl +pZegYhTbunTVv1KKJ+5n1hY6pG/Jr8oLY3b9i4qwDLKfQGm5PmrfwAtqbLSfY2M0 +2AZyAhCdGbqB7WpTdG1J7DzGbVVWAtS05e24Mu0qZJvpHdtl4+t89vXgJ/bPrPxF +cpAlT9DOtobTEqrXZeS937F1qNyIgyBki+7mtxkwng5cf3zQM2BJ9lSFQJOBSRDr +haMcnaPI4pknO7OfYf5W9LaS1Dx/U/NeMBfnVBd9NjUw+TMjy2MdMLUaLa9EF7Jo +Gjk+fKaTaUgO8I487wHPMeoEA4A4dEePzGrybRLfl1ZYGQ0xcgunz64n2xfQIy2y +swiyaofYlLxzHzOL0N+Y76P0ic37t9R2F5ggNhfbXhClK2h4HmdjRRRt3VkxR4AD +7OM09bEhlZby34HOlCaC0PHKwYBMjneAG3ycPN88YTMYR2/KizExe71ayNwX2KHL +ib1nOZgZT6s+YvgsZ7lRmMD4iqjuAEh5SRAcWlolVif8bAy09BkY1vwrtgV73q88 +heEbsCE1fsfk1OfH5W4yjjiSDZFRt5oTCPQWJp+2P0RJ9LCxcbf0RrCg3hg5rD9N +lVTA0dsixv5zF3wTuad9inhk9Rmlq1KoaqA= +-----END CERTIFICATE----- diff --git a/make/common/templates/notary/notary-signer.crt b/make/common/templates/notary/notary-signer.crt new file mode 100644 index 000000000..1189dfd7b --- /dev/null +++ b/make/common/templates/notary/notary-signer.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFdjCCA14CCQCeVwANSZmmiDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC +VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCVBhbG8gQWx0bzEVMBMG +A1UECgwMVk13YXJlLCBJbmMuMQ8wDQYDVQQLDAZIYXJib3IxJDAiBgNVBAMMG1Nl +bGYtc2lnbmVkIGJ5IFZNd2FyZSwgSW5jLjAeFw0xNzAzMjQwNTMyMDBaFw0yNzAz +MjIwNTMyMDBaMHUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIw +EAYDVQQHDAlQYWxvIEFsdG8xFTATBgNVBAoMDFZNd2FyZSwgSW5jLjEPMA0GA1UE +CwwGSGFyYm9yMRUwEwYDVQQDDAxub3RhcnlzaWduZXIwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQC6TV2RCoH8d1g6xFvDo4FL9v+pGLe5+bu9ryjTaLbN +dH/Cmf5/8WrmgJ3vG2Ksk796J7qsVddwvQkZn6NwDm2Tm+ETMCG85yEA3jl4Kr9R +XfWHYWEavv0vsq6M+bUSSq7VJAhgk4wfx6qJBnFX2qKpODeYLHaHxU1EnIXrStNf +IqR4Eu0Xre8jAkzrDdaFy/KnX4HGgNdz413CXzBCKEuu3VJj07ZvonnTzOgoLvh8 ++PCoQ2M4OBPT9gHqUov1I8nWnrjc+HuM1BW3YIGCB5TV9x0Y7hjvkr4E38gbJURj +uDwg8jof4lMRmU/FHXFLt1ucGwNFUJdPwI7dyEKRA03Lr7htfP5sa9tmv3L93dKD +po1gW1LsfiM3Cur5jARM/hBA+eYJr12Laf9oL59r8JmweqF3zRSwGSY336XoR/Fv +/PAFs9vfKKWZp0uiRtuY9JZNRTF8trnfNf1957bND+DS2HWPmWkw4yK6CGa0s55X +adiDt4gDFvKjl68dBWZoHutY+cZy/hK1D5uqagcX1kzbr/Pzy1gsq9FBBwaTJqBu +YIAsSuzP+7NNZXoPd3rg13V93pbZr8eQN5VOQIBZK83xZEtHSJBEdUSuBOo3JS7j +/rjEnspRqOI4soFnx1vaK0TrRyzJ5KBOuGpW4u8/ZUdIq8KIE30Mj/XI/sgAPr5j +UQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBjqYBm/FRqyMH2hnHA0TMXY/WPufJ8 +TX10daELCAYJCEETXmUt1i7dnFxdAZXTnHENHdNYiS4nGBfqMLmODtcAamcv6Dcl +JnyQPt3QlCDPKkcHgz3y4tvDDx6M5rFWYzN9QLiWAYrunIk1R4Jj7FODrM6/NODE +0Mz1czWfsmLfX/jF80SsxnY1DCLKGgo6/RID3xTp4eIMboxCfeH2/yDA+6YPyYbV +Si4ccwo9Foq0IYU8bimPNTyBQ0N+8ajcn328ql6aazmr894Ch5pWA3Qxaa98FcKS +zokBvmmCuvCJ9HOmxKWdFEhSRS9GWxn7wg78UIlLP/8RfUrsecBJHgyhWRA7Qs3K +keiG68Zrhn456IdMxjCZXgJ7gAAe77n4Cz8sFEHAvnAg9JLNEHuEBV5H1Hb7TzET +k0lPiEY78QjutOpqHsWiagqSjlGEMqKI9c8WxXHh9030T/6NnWkdXFo+4HaEZEpp +0JryASS53B5SwLIPrn0Y2/io/kRgbglGktPt6Ex0DwW3f96lcz3me34Nw+HOYYnz +b0cz7JqJZgFXfEnykic3IwZs7m7Xrl9B/vvaVub9Fb5LQ7rIzrO7VkoILov/G41B +Pd4/kagjXDTWd+UBMvZF6YGjr+TUZi5ooi7bvQ3X6N9WNYKW4a1DOokz9janStiL +MrTKyOEOBi0Aew== +-----END CERTIFICATE----- diff --git a/make/common/templates/notary/notary-signer.key b/make/common/templates/notary/notary-signer.key new file mode 100644 index 000000000..3973cec7b --- /dev/null +++ b/make/common/templates/notary/notary-signer.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC6TV2RCoH8d1g6 +xFvDo4FL9v+pGLe5+bu9ryjTaLbNdH/Cmf5/8WrmgJ3vG2Ksk796J7qsVddwvQkZ +n6NwDm2Tm+ETMCG85yEA3jl4Kr9RXfWHYWEavv0vsq6M+bUSSq7VJAhgk4wfx6qJ +BnFX2qKpODeYLHaHxU1EnIXrStNfIqR4Eu0Xre8jAkzrDdaFy/KnX4HGgNdz413C +XzBCKEuu3VJj07ZvonnTzOgoLvh8+PCoQ2M4OBPT9gHqUov1I8nWnrjc+HuM1BW3 +YIGCB5TV9x0Y7hjvkr4E38gbJURjuDwg8jof4lMRmU/FHXFLt1ucGwNFUJdPwI7d +yEKRA03Lr7htfP5sa9tmv3L93dKDpo1gW1LsfiM3Cur5jARM/hBA+eYJr12Laf9o +L59r8JmweqF3zRSwGSY336XoR/Fv/PAFs9vfKKWZp0uiRtuY9JZNRTF8trnfNf19 +57bND+DS2HWPmWkw4yK6CGa0s55XadiDt4gDFvKjl68dBWZoHutY+cZy/hK1D5uq +agcX1kzbr/Pzy1gsq9FBBwaTJqBuYIAsSuzP+7NNZXoPd3rg13V93pbZr8eQN5VO +QIBZK83xZEtHSJBEdUSuBOo3JS7j/rjEnspRqOI4soFnx1vaK0TrRyzJ5KBOuGpW +4u8/ZUdIq8KIE30Mj/XI/sgAPr5jUQIDAQABAoICAQCqIgbFcqwcK7zWBgWrFsD3 +53u4J4t4+df6NGB7F9CAtdgKlej1XDl8gI46Em89HLwqyOdPhCD3opoR3Vg69+IX +f62+gSD+SrA4A7jFxXvryXt0g3hTHYFHssx2j39NUghxOrOvxm6bgxJ4ifqt+Uq8 +cEtM26Xu/T4/3xTpN+7pnVBHGzmLe1q8RNiLe5qhmwtgz/ZKmdSnz0YLQDRo5jWf +Xhxkb63WKrFIu4JzV9my/v9/GfMdHxD0a196ZqHLX0Buj4pQuVbS18dxLF94qIXC +FCZtYtpAxmhjOR2btJ/M1S2MBMkR3vRvSOuxHd8d/zdYys5k2WElArs1TDGGDldW +jp3FYkoygsdWTs056HM1Y9F8dV2KAWfAhEQD8mBIGVjMrCqpnyZcK6JkqVg9c7YW +IYQ2JRwsHq58FMNa3TLTvf/OClhEfSbRWAF0AhMTpnSUgP06cbJeXyzqzHdE37hv +74OBx7KNoS+PEQ3lVgbHsWoUzf3SqB1IOzLyzuEUgHqON2GKmmCNcRMBi3DuV9tw +Q8LWynNxhD8vyBkmo0kAd/FwgXrxJTGdYvxyn29I7QanCTH7o8wtjSE0jj9Qo7oC +McAYGR6oTAjrT78KhI7aZJU5nuA6ySSCJRa6et1CC+SseWknyMMJ5HTo8l7jjXJA +9hjNGGs6giOxznizf+2YAQKCAQEA9wRQk4yN402tfuicvfQBnFUtcpqctWSgGc0T +qzWJgH/W07FMUHzAvqCgsYMMaeteXOMZH7jijvtIlhYfIg5w+RJ9PSsSu680OzGN +R31+l2B/QzRAHUJ6+OVgWxAn6awU1mYLaiwVmSNWEnjAPE4XeSK708OOganI3pBQ +8zOHj+j6uV8ddG79D6FqNJHAQwpou/p+XO/BGDFgX22x4F68Z0gCQcmoyAE7ppOp +dqq3lPoDbRQ02/5cqaIA6dhmfjK2cpz4y1nUxffzY7qJjpoB/YSdR66cCNiYcJzp +fMVBXhF9Iyj/Cah1w+hc0NOy9dW15afFaLFK0zrtAzEaVxH/0QKCAQEAwRPOwSCl +XrMYXmc91TF6XbhErILHK/pIEOIMF09KNJvSjY0188Ram/pFbPRYh0cIyASmRGXL +Qq5B1Qi0vx5TCq1OCrW2yeE7zboAlnADhk1u9N8YmL6JrCKVGQO7wFD3V8uphXdM +tixNa5WvJ6eE5Vq+SVy99V5pQgb8ErrISlW4MYK7LI7DruSDuM2tHtiOcXcdTVej +1stXJZkH46RYvxxid9tRzfiB8K5ziZfLwPNf2wRyj1J4ojn5pPNhhfkjJ24LCZGt +JxwSXqdP+4x7by6x3mU+hutU/lF3jl+0edSnU0cZ6lvuq2T5YGgda/VXlv1ZFQUw +rwUXD9unU+aLgQKCAQEA9R74/pI5sthAVHFsKStb9dComtNGstI59aCF5h3oZvV1 +Lvj/q9dARWqMS9qplOoV58MMCWikmhJNw3IMTvVZsjBgyzRVEJ4aDKttcQXde0Ys +w3m0LdTsxtSHu5XapY032FHG/gLlI+Pm48mjqbQsou6OyOOEJLNhO0qmqc/2tB4T +v6PdTM9enAYnqCcCTQSlTfSTNJJOYT2OTuRB4U7hUvQoGTSOInrmwLRDNBjQuCso +/zNQCQbu2P6EPYmam5yjZDTUxqZL+G/GvK49Fp9JXlQc5ycke7rD+uwa3s+3wCtG +rH9gJitfQZrxj+Cj9EOwj0bfJLbac6ZD0CkH5GNeIQKCAQBdoGFOPapzdZ2HicDu +NQQFlmmWzgQPS1rO9Q6v7v8o67b6dVOIVdsqb/5ii0qyrruPYtHNsR8TwrShvYsI +cogKUWfawatV0ibR6DSIvuC2q632iIjA6QSRuGNcsfbFl32Z0WTvF57XaDxSw08g +h5dmMM69fH+REKsyHXj3DCQ8B70+JQrm3IP/t0g4wWQF5TWNyBkpfCoy6n/j94Vf +2j4+zmDhhjTxEGTSdYYJXtarRllhN5Ll9TQSVtK8LllIQjvNzwsDJOU2ZeJyi+e5 +L7Jbg+U01xuvCUc52/+Bxt8ZhQlu1Le4ccQW0Ows19AMnfhPe6NLEi09cdZxFi7Z +/J4BAoIBABCzkBDFxZdfWYt69VBt9PSG8eJ6avny3hXCtKaHIQb+aD5nKjRP0DVh +gyutCo6RasMEc6D1tJGyR/Xvhm64q4JPb5UbSaRQiVYKdgRtMM9pZeBkcBtNs18K +yMx5ajgYorrbi86hXHX7q+JYP8MCbcqqAUSl/Hi8nPxc1foTiCNDf4kGoHvXmoxt +0tA65tFFQhEA6KBn68SDkyTsl/zb5Sx0GJY4kZkOeF3GaxPFX12skgXv95GJUskX +88RJsH4Qqqtzbzj8R241BH8OrcOoyELc6xPioEqUHKVxSIf2ylITbj0UQHd2u0mN +tajKl+aoc+CDxUYbilzhhKetWWF/cJY= +-----END PRIVATE KEY----- diff --git a/make/common/templates/notary/server-config.json b/make/common/templates/notary/server-config.json new file mode 100644 index 000000000..dc8ffba31 --- /dev/null +++ b/make/common/templates/notary/server-config.json @@ -0,0 +1,28 @@ +{ + "server": { + "http_addr": ":4443" + }, + "trust_service": { + "type": "remote", + "hostname": "notarysigner", + "port": "7899", + "tls_ca_file": "./notary-signer-ca.crt", + "key_algorithm": "ecdsa" + }, + "logging": { + "level": "debug" + }, + "storage": { + "backend": "mysql", + "db_url": "server@tcp(mysql:3306)/notaryserver?parseTime=True" + }, + "auth": { + "type": "token", + "options": { + "realm": "$token_endpoint/service/token", + "service": "harbor-notary", + "issuer": "harbor-token-issuer", + "rootcertbundle": "/config/root.crt" + } + } +} diff --git a/make/common/templates/notary/signer-config.json b/make/common/templates/notary/signer-config.json new file mode 100644 index 000000000..80c095488 --- /dev/null +++ b/make/common/templates/notary/signer-config.json @@ -0,0 +1,15 @@ +{ + "server": { + "grpc_addr": ":7899", + "tls_cert_file": "./notary-signer.crt", + "tls_key_file": "./notary-signer.key" + }, + "logging": { + "level": "debug" + }, + "storage": { + "backend": "mysql", + "db_url": "signer@tcp(mysql:3306)/notarysigner?parseTime=True", + "default_alias":"defaultalias" + } +} diff --git a/make/common/templates/notary/signer_env b/make/common/templates/notary/signer_env new file mode 100644 index 000000000..23a1d679d --- /dev/null +++ b/make/common/templates/notary/signer_env @@ -0,0 +1,2 @@ +NOTARY_SIGNER_DEFAULTALIAS=$alias + diff --git a/make/common/templates/registry/config.yml b/make/common/templates/registry/config.yml index faabe10cd..9049c6fa9 100644 --- a/make/common/templates/registry/config.yml +++ b/make/common/templates/registry/config.yml @@ -20,10 +20,10 @@ http: addr: localhost:5001 auth: token: - issuer: registry-token-issuer + issuer: harbor-token-issuer realm: $ui_url/service/token rootcertbundle: /etc/registry/root.crt - service: token-service + service: harbor-registry notifications: endpoints: diff --git a/make/common/templates/ui/app.conf b/make/common/templates/ui/app.conf index 3cda6d877..8e8f199b7 100644 --- a/make/common/templates/ui/app.conf +++ b/make/common/templates/ui/app.conf @@ -1,18 +1,6 @@ -appname = registry +appname = Harbor runmode = dev - -[lang] -types = en-US|zh-CN -names = en-US|zh-CN +enablegzip = true [dev] httpport = 80 - -[mail] -identity = $email_identity -host = $email_server -port = $email_server_port -username = $email_username -password = $email_password -from = $email_from -ssl = $email_ssl diff --git a/make/common/templates/ui/env b/make/common/templates/ui/env index e4feedc2e..1f457e257 100644 --- a/make/common/templates/ui/env +++ b/make/common/templates/ui/env @@ -1,29 +1,5 @@ -MYSQL_HOST=mysql -MYSQL_PORT=3306 -MYSQL_USR=root -MYSQL_PWD=$db_password -REGISTRY_URL=http://registry:5000 -JOB_SERVICE_URL=http://jobservice -UI_URL=http://ui -CONFIG_PATH=/etc/ui/app.conf -EXT_REG_URL=$hostname -HARBOR_ADMIN_PASSWORD=$harbor_admin_password -AUTH_MODE=$auth_mode -LDAP_URL=$ldap_url -LDAP_SEARCH_DN=$ldap_searchdn -LDAP_SEARCH_PWD=$ldap_search_pwd -LDAP_BASE_DN=$ldap_basedn -LDAP_FILTER=$ldap_filter -LDAP_UID=$ldap_uid -LDAP_SCOPE=$ldap_scope -UI_SECRET=$ui_secret -SECRET_KEY=$secret_key -SELF_REGISTRATION=$self_registration -USE_COMPRESSED_JS=$use_compressed_js LOG_LEVEL=debug +CONFIG_PATH=/etc/ui/app.conf +UI_SECRET=$ui_secret +JOBSERVICE_SECRET=$jobservice_secret GODEBUG=netdns=cgo -EXT_ENDPOINT=$ui_url -TOKEN_ENDPOINT=http://ui -VERIFY_REMOTE_CERT=$verify_remote_cert -TOKEN_EXPIRATION=$token_expiration -PROJECT_CREATION_RESTRICTION=$project_creation_restriction diff --git a/make/dev/adminserver/Dockerfile b/make/dev/adminserver/Dockerfile new file mode 100644 index 000000000..d05f87da2 --- /dev/null +++ b/make/dev/adminserver/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.7.3 + +MAINTAINER yinw@vmware.com + +COPY . /go/src/github.com/vmware/harbor + +WORKDIR /go/src/github.com/vmware/harbor/src/adminserver + +RUN go build -v -a -o /go/bin/harbor_adminserver \ + && chmod u+x /go/bin/harbor_adminserver +WORKDIR /go/bin/ +ENTRYPOINT ["/go/bin/harbor_adminserver"] diff --git a/make/dev/docker-compose.yml b/make/dev/docker-compose.yml index 1d8e9f858..3b1b80906 100644 --- a/make/dev/docker-compose.yml +++ b/make/dev/docker-compose.yml @@ -3,18 +3,24 @@ services: log: build: context: ../../ - dockerfile: make/ubuntu/log/Dockerfile + dockerfile: make/photon/log/Dockerfile + container_name: harbor-log restart: always volumes: - - /var/log/harbor/:/var/log/docker/ + - /var/log/harbor/:/var/log/docker/:z ports: - - 1514:514 + - 127.0.0.1:1514:514 + networks: + - harbor registry: - image: library/registry:2.5.0 + image: vmware/registry:photon-2.6.0 + container_name: registry restart: always volumes: - - /data/registry:/storage - - ../common/config/registry/:/etc/registry/ + - /data/registry:/storage:z + - ./common/config/registry/:/etc/registry/:z + networks: + - harbor environment: - GODEBUG=netdns=cgo command: @@ -23,65 +29,105 @@ services: - log logging: driver: "syslog" - options: + options: syslog-address: "tcp://127.0.0.1:1514" tag: "registry" mysql: build: ../common/db/ + container_name: harbor-db restart: always volumes: - - /data/database:/var/lib/mysql + - /data/database:/var/lib/mysql:z + networks: + - harbor env_file: - ../common/config/db/env depends_on: - log logging: driver: "syslog" - options: + options: syslog-address: "tcp://127.0.0.1:1514" tag: "mysql" + adminserver: + build: + context: ../../ + dockerfile: make/dev/adminserver/Dockerfile + container_name: harbor-adminserver + env_file: + - ../common/config/adminserver/env + restart: always + volumes: + - /data/config/:/etc/adminserver/config/:z + - /data/secretkey:/etc/adminserver/key:z + - /data/:/data/:z + depends_on: + - log + networks: + - harbor + logging: + driver: "syslog" + options: + syslog-address: "tcp://127.0.0.1:1514" + tag: "adminserver" ui: build: context: ../../ dockerfile: make/dev/ui/Dockerfile + container_name: harbor-ui env_file: - ../common/config/ui/env restart: always volumes: - - ../common/config/ui/app.conf:/etc/ui/app.conf - - ../common/config/ui/private_key.pem:/etc/ui/private_key.pem + - ./common/config/ui/app.conf:/etc/ui/app.conf:z + - ./common/config/ui/private_key.pem:/etc/ui/private_key.pem:z + - /data/secretkey:/etc/ui/key:z + - /data/ca_download/:/etc/ui/ca/:z + networks: + - harbor depends_on: - log + - adminserver + - registry logging: driver: "syslog" - options: + options: syslog-address: "tcp://127.0.0.1:1514" tag: "ui" jobservice: build: context: ../../ dockerfile: make/dev/jobservice/Dockerfile + container_name: harbor-jobservice env_file: - ../common/config/jobservice/env restart: always volumes: - - /data/job_logs:/var/log/jobs - - ../common/config/jobservice/app.conf:/etc/jobservice/app.conf + - /data/job_logs:/var/log/jobs:z + - ./common/config/jobservice/app.conf:/etc/jobservice/app.conf:z + - /data/secretkey:/etc/jobservice/key:z + networks: + - harbor depends_on: - ui + - adminserver logging: driver: "syslog" - options: + options: syslog-address: "tcp://127.0.0.1:1514" tag: "jobservice" proxy: - image: library/nginx:1.11.5 + image: vmware/nginx:1.11.5-patched + container_name: nginx restart: always volumes: - - ../common/config/nginx:/etc/nginx + - ./common/config/nginx:/etc/nginx:z + networks: + - harbor ports: - 80:80 - 443:443 + - 4443:4443 depends_on: - mysql - registry @@ -89,6 +135,9 @@ services: - log logging: driver: "syslog" - options: + options: syslog-address: "tcp://127.0.0.1:1514" tag: "proxy" +networks: + harbor: + external: false diff --git a/make/dev/jobservice/Dockerfile b/make/dev/jobservice/Dockerfile index e9e525d3c..87bc159fb 100644 --- a/make/dev/jobservice/Dockerfile +++ b/make/dev/jobservice/Dockerfile @@ -1,11 +1,7 @@ -FROM golang:1.6.2 +FROM golang:1.7.3 MAINTAINER jiangd@vmware.com -RUN apt-get update \ - && apt-get install -y libldap2-dev \ - && rm -r /var/lib/apt/lists/* - COPY . /go/src/github.com/vmware/harbor WORKDIR /go/src/github.com/vmware/harbor/src/jobservice diff --git a/make/dev/nodeclarity/Dockerfile b/make/dev/nodeclarity/Dockerfile new file mode 100644 index 000000000..30a167cdd --- /dev/null +++ b/make/dev/nodeclarity/Dockerfile @@ -0,0 +1,18 @@ +FROM node:7.5.0 + +RUN mkdir -p /clarity-seed + +COPY src/ui_ng/package.json /clarity-seed +COPY src/ui_ng/tslint.json /clarity-seed +COPY src/ui_ng/typings.json /clarity-seed +COPY src/ui_ng/yarn.lock /clarity-seed +COPY make/dev/nodeclarity/angular-cli.json /clarity-seed +COPY make/dev/nodeclarity/entrypoint.sh / + +WORKDIR /clarity-seed + +RUN npm install -g @angular/cli && \ + npm install && \ + chmod u+x /entrypoint.sh + +VOLUME ["/clarity-seed", "/clarity-seed/dist"] \ No newline at end of file diff --git a/make/dev/nodeclarity/angular-cli.json b/make/dev/nodeclarity/angular-cli.json new file mode 100644 index 000000000..bba385099 --- /dev/null +++ b/make/dev/nodeclarity/angular-cli.json @@ -0,0 +1,67 @@ +{ + "project": { + "version": "1.0.0-beta.20-4", + "name": "clarity-seed" + }, + "apps": [ + { + "root": "src", + "outDir": "dist", + "assets": [ + "images", + "favicon.ico" + ], + "index": "index.html", + "main": "main.ts", + "test": "test.ts", + "tsconfig": "tsconfig.json", + "prefix": "app", + "mobile": false, + "styles": [ + "../node_modules/clarity-icons/clarity-icons.min.css", + "../node_modules/clarity-ui/clarity-ui.min.css", + "styles.css" + ], + "scripts": [ + "../node_modules/core-js/client/shim.min.js", + "../node_modules/mutationobserver-shim/dist/mutationobserver.min.js", + "../node_modules/@webcomponents/custom-elements/custom-elements.min.js", + "../node_modules/clarity-icons/clarity-icons.min.js", + "../node_modules/web-animations-js/web-animations.min.js" + ], + "environmentSource": "environments/environment.ts", + "environments": { + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + } + ], + "addons": [], + "packages": [], + "e2e": { + "protractor": { + "config": "./protractor.config.js" + } + }, + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "scss", + "prefixInterfaces": false, + "inline": { + "style": false, + "template": false + }, + "spec": { + "class": false, + "component": true, + "directive": true, + "module": false, + "pipe": true, + "service": true + } + } +} diff --git a/make/dev/nodeclarity/entrypoint.sh b/make/dev/nodeclarity/entrypoint.sh new file mode 100644 index 000000000..25bcc6df3 --- /dev/null +++ b/make/dev/nodeclarity/entrypoint.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +cd /clarity-seed +rm -rf dist/* + +npm_proxy= + +while getopts p: option +do + case "${option}" + in + p) npm_proxy=${OPTARG};; + esac +done + +if [ ! -z "$npm_proxy" -a "$npm_proxy" != " " ]; then + npm config set proxy $npm_proxy +fi + +npm install +ng build + +cp -r ./src/i18n/ dist/ + diff --git a/make/dev/nodeclarity/index.html b/make/dev/nodeclarity/index.html new file mode 100644 index 000000000..37df927a2 --- /dev/null +++ b/make/dev/nodeclarity/index.html @@ -0,0 +1,13 @@ + + + + + Clarity Seed App + + + + + +Loading... + + diff --git a/make/dev/ui/Dockerfile b/make/dev/ui/Dockerfile index dfa7ec5fa..47f24c954 100644 --- a/make/dev/ui/Dockerfile +++ b/make/dev/ui/Dockerfile @@ -1,31 +1,20 @@ -FROM golang:1.6.2 +FROM golang:1.7.3 MAINTAINER jiangd@vmware.com -RUN apt-get update \ - && apt-get install -y libldap2-dev \ - && rm -r /var/lib/apt/lists/* - COPY src/. /go/src/github.com/vmware/harbor/src WORKDIR /go/src/github.com/vmware/harbor/src/ui RUN go build -v -a -o /go/bin/harbor_ui -ENV MYSQL_USR root \ - MYSQL_PWD root \ - REGISTRY_URL localhost:5000 - COPY src/ui/views /go/bin/views COPY src/ui/static /go/bin/static COPY src/favicon.ico /go/bin/favicon.ico -COPY make/jsminify.sh /tmp/jsminify.sh -RUN chmod u+x /go/bin/harbor_ui \ - && sed -i 's/TLS_CACERT/#TLS_CAERT/g' /etc/ldap/ldap.conf \ - && sed -i '$a\TLS_REQCERT allow' /etc/ldap/ldap.conf \ - && timestamp=`date '+%s'` \ - && /tmp/jsminify.sh /go/bin/views/sections/script-include.htm /go/bin/static/resources/js/harbor.app.min.$timestamp.js /go/bin/ \ - && sed -i "s/harbor\.app\.min\.js/harbor\.app\.min\.$timestamp\.js/g" /go/bin/views/sections/script-min-include.htm +RUN mkdir /go/bin/harbor/ +COPY VERSION /go/bin/harbor/VERSION + +RUN chmod u+x /go/bin/harbor_ui WORKDIR /go/bin/ ENTRYPOINT ["/go/bin/harbor_ui"] diff --git a/make/docker-compose.notary.yml b/make/docker-compose.notary.yml new file mode 100644 index 000000000..102bf3641 --- /dev/null +++ b/make/docker-compose.notary.yml @@ -0,0 +1,76 @@ +version: '2' +services: + ui: + networks: + - harbor-notary + proxy: + networks: + - harbor-notary + notary-server: + image: vmware/notary-photon:server-0.5.0 + container_name: notary-server + networks: + - notary-mdb + - notary-sig + - harbor-notary + volumes: + - ./common/config/notary:/config + entrypoint: /usr/bin/env sh + command: -c "/migrations/migrate.sh && notary-server -config=/config/server-config.json -logf=logfmt" + depends_on: + - notary-db + - notary-signer + logging: + driver: "syslog" + options: + syslog-address: "tcp://127.0.0.1:1514" + tag: "notary-server" + notary-signer: + image: vmware/notary-photon:signer-0.5.0 + container_name: notary-signer + networks: + notary-mdb: + notary-sig: + aliases: + - notarysigner + volumes: + - ./common/config/notary:/config + env_file: + - ./common/config/notary/signer_env + entrypoint: /usr/bin/env sh + command: -c "/migrations/migrate.sh && notary-signer -config=/config/signer-config.json -logf=logfmt" + depends_on: + - notary-db + logging: + driver: "syslog" + options: + syslog-address: "tcp://127.0.0.1:1514" + tag: "notary-signer" + notary-db: + image: vmware/harbor-notary-db:mariadb-10.1.10 + container_name: notary-db + networks: + notary-mdb: + aliases: + - mysql + volumes: + - ./common/config/notary/mysql-initdb.d:/docker-entrypoint-initdb.d + - /data/notary-db:/var/lib/mysql + environment: + - TERM=dumb + - MYSQL_ALLOW_EMPTY_PASSWORD="true" + command: mysqld --innodb_file_per_table + depends_on: + - log + logging: + driver: "syslog" + options: + syslog-address: "tcp://127.0.0.1:1514" + tag: "notary-db" +networks: + harbor-notary: + external: false + notary-mdb: + external: false + notary-sig: + external: false diff --git a/make/docker-compose.tpl b/make/docker-compose.tpl index dfe1cc8e9..bf59cd84c 100644 --- a/make/docker-compose.tpl +++ b/make/docker-compose.tpl @@ -1,20 +1,24 @@ version: '2' services: log: - image: vmware/harbor-log + image: vmware/harbor-log:__version__ container_name: harbor-log restart: always volumes: - - /var/log/harbor/:/var/log/docker/ + - /var/log/harbor/:/var/log/docker/:z ports: - - 1514:514 + - 127.0.0.1:1514:514 + networks: + - harbor registry: - image: library/registry:2.5.0 + image: vmware/registry:photon-2.6.0 container_name: registry restart: always volumes: - - /data/registry:/storage - - ./common/config/registry/:/etc/registry/ + - /data/registry:/storage:z + - ./common/config/registry/:/etc/registry/:z + networks: + - harbor environment: - GODEBUG=netdns=cgo command: @@ -27,11 +31,13 @@ services: syslog-address: "tcp://127.0.0.1:1514" tag: "registry" mysql: - image: vmware/harbor-db + image: vmware/harbor-db:__version__ container_name: harbor-db restart: always volumes: - - /data/database:/var/lib/mysql + - /data/database:/var/lib/mysql:z + networks: + - harbor env_file: - ./common/config/db/env depends_on: @@ -41,48 +47,79 @@ services: options: syslog-address: "tcp://127.0.0.1:1514" tag: "mysql" - ui: - image: vmware/harbor-ui - container_name: harbor-ui + adminserver: + image: vmware/harbor-adminserver:__version__ + container_name: harbor-adminserver env_file: - - ./common/config/ui/env + - ./common/config/adminserver/env restart: always volumes: - - ./common/config/ui/app.conf:/etc/ui/app.conf - - ./common/config/ui/private_key.pem:/etc/ui/private_key.pem - - /data:/harbor_storage + - /data/config/:/etc/adminserver/config/:z + - /data/secretkey:/etc/adminserver/key:z + - /data/:/data/:z + networks: + - harbor depends_on: - log + logging: + driver: "syslog" + options: + syslog-address: "tcp://127.0.0.1:1514" + tag: "adminserver" + ui: + image: vmware/harbor-ui:__version__ + container_name: harbor-ui + env_file: + - ./common/config/ui/env + restart: always + volumes: + - ./common/config/ui/app.conf:/etc/ui/app.conf:z + - ./common/config/ui/private_key.pem:/etc/ui/private_key.pem:z + - /data/secretkey:/etc/ui/key:z + - /data/ca_download/:/etc/ui/ca/:z + networks: + - harbor + depends_on: + - log + - adminserver + - registry logging: driver: "syslog" options: syslog-address: "tcp://127.0.0.1:1514" tag: "ui" jobservice: - image: vmware/harbor-jobservice + image: vmware/harbor-jobservice:__version__ container_name: harbor-jobservice env_file: - ./common/config/jobservice/env restart: always volumes: - - /data/job_logs:/var/log/jobs - - ./common/config/jobservice/app.conf:/etc/jobservice/app.conf + - /data/job_logs:/var/log/jobs:z + - ./common/config/jobservice/app.conf:/etc/jobservice/app.conf:z + - /data/secretkey:/etc/jobservice/key:z + networks: + - harbor depends_on: - ui + - adminserver logging: driver: "syslog" options: syslog-address: "tcp://127.0.0.1:1514" tag: "jobservice" proxy: - image: nginx:1.11.5 + image: vmware/nginx:1.11.5-patched container_name: nginx restart: always volumes: - - ./common/config/nginx:/etc/nginx + - ./common/config/nginx:/etc/nginx:z + networks: + - harbor ports: - 80:80 - 443:443 + - 4443:4443 depends_on: - mysql - registry @@ -93,3 +130,7 @@ services: options: syslog-address: "tcp://127.0.0.1:1514" tag: "proxy" +networks: + harbor: + external: false + diff --git a/make/harbor.cfg b/make/harbor.cfg index d5475339c..f720ab76d 100644 --- a/make/harbor.cfg +++ b/make/harbor.cfg @@ -8,6 +8,34 @@ hostname = reg.mydomain.com #It can be set to https if ssl is enabled on nginx. ui_url_protocol = http +#The password for the root user of mysql db, change this before any production use. +db_password = root123 + +#Maximum number of job workers in job service +max_job_workers = 3 + +#Determine whether or not to generate certificate for the registry's token. +#If the value is on, the prepare script creates new root cert and private key +#for generating token to access the registry. If the value is off the default key/cert will be used. +#This flag also controls the creation of the notary signer's cert. +customize_crt = on + +#The path of cert and key files for nginx, they are applied only the protocol is set to https +ssl_cert = /data/cert/server.crt +ssl_cert_key = /data/cert/server.key + +#The path of secretkey storage +secretkey_path = /data + +#Admiral's url, comment this attribute, or set its value to NA when Harbor is standalone +admiral_url = NA + +#NOTES: The properties between BEGIN INITIAL PROPERTIES and END INITIAL PROPERTIES +#only take effect in the first boot, the subsequent changes of these properties +#should be performed on web ui + +#************************BEGIN INITIAL PROPERTIES************************ + #Email account settings for sending out password resetting emails. #Email server uses the given username and password to authenticate on TLS connections to host and act as identity. @@ -52,46 +80,23 @@ ldap_uid = uid #the scope to search for users, 1-LDAP_SCOPE_BASE, 2-LDAP_SCOPE_ONELEVEL, 3-LDAP_SCOPE_SUBTREE ldap_scope = 3 -#The password for the root user of mysql db, change this before any production use. -db_password = root123 +#Timeout (in seconds) when connecting to an LDAP Server. The default value (and most reasonable) is 5 seconds. +ldap_timeout = 5 #Turn on or off the self-registration feature self_registration = on -#Determine whether the UI should use compressed js files. -#For production, set it to on. For development, set it to off. -use_compressed_js = on - -#Maximum number of job workers in job service -max_job_workers = 3 - #The expiration time (in minute) of token created by token service, default is 30 minutes token_expiration = 30 +#The flag to control what users have permission to create projects +#The default value "everyone" allows everyone to creates a project. +#Set to "adminonly" so that only admin user can create project. +project_creation_restriction = everyone + #Determine whether the job service should verify the ssl cert when it connects to a remote registry. #Set this flag to off when the remote registry uses a self-signed or untrusted certificate. verify_remote_cert = on - -#Determine whether or not to generate certificate for the registry's token. -#If the value is on, the prepare script creates new root cert and private key -#for generating token to access the registry. If the value is off, a key/certificate must -#be supplied for token generation. -customize_crt = on - -#Information of your organization for certificate -crt_country = CN -crt_state = State -crt_location = CN -crt_organization = organization -crt_organizationalunit = organizational unit -crt_commonname = example.com -crt_email = example@example.com - -#The flag to control what users have permission to create projects -#Be default everyone can create a project, set to "adminonly" such that only admin can create project. -project_creation_restriction = everyone - -#The path of cert and key files for nginx, they are applied only the protocol is set to https -ssl_cert = /data/cert/server.crt -ssl_cert_key = /data/cert/server.key +#************************END INITIAL PROPERTIES************************ ############# + diff --git a/make/install.sh b/make/install.sh index f4f7592a3..d1e1bd994 100755 --- a/make/install.sh +++ b/make/install.sh @@ -49,14 +49,20 @@ note() { printf "\n${underline}${bold}${blue}Note:${reset} ${blue}%s${reset}\n" set -e set +o noglob -usage=$'Please set hostname and other necessary attributes in harbor.cfg first. DO NOT use localhost or 127.0.0.1 for hostname, because Harbor needs to be accessed by external clients.' +usage=$'Please set hostname and other necessary attributes in harbor.cfg first. DO NOT use localhost or 127.0.0.1 for hostname, because Harbor needs to be accessed by external clients. +Please set --with-notary if needs enable Notary in Harbor, and set ui_url_protocol/ssl_cert/ssl_cert_key in harbor.cfg bacause notary must run under https.' item=0 +# notary is not enabled by default +with_notary=$false + while [ $# -gt 0 ]; do case $1 in --help) note "$usage" exit 0;; + --with-notary) + with_notary=true;; *) note "$usage" exit 1;; @@ -134,10 +140,10 @@ h2 "[Step $item]: checking installation environment ..."; let item+=1 check_docker check_dockercompose -if [ -f harbor*.tgz ] +if [ -f harbor*.tar.gz ] then h2 "[Step $item]: loading Harbor images ..."; let item+=1 - docker load -i ./harbor*.tgz + docker load -i ./harbor*.tar.gz fi echo "" @@ -146,19 +152,38 @@ if [ -n "$host" ] then sed "s/^hostname = .*/hostname = $host/g" -i ./harbor.cfg fi -./prepare +if [ $with_notary ] +then + ./prepare --with-notary +else + ./prepare +fi echo "" h2 "[Step $item]: checking existing instance of Harbor ..."; let item+=1 -if [ -n "$(docker-compose -f docker-compose*.yml ps -q)" ] -then - note "stopping existing Harbor instance ..." - docker-compose -f docker-compose*.yml down +if [ $with_notary ] +then + if [ -n "$(docker-compose -f docker-compose.yml -f docker-compose.notary.yml ps -q)" ] + then + note "stopping existing Harbor instance ..." + docker-compose -f docker-compose.yml -f docker-compose.notary.yml down -v + fi +else + if [ -n "$(docker-compose -f docker-compose.yml ps -q)" ] + then + note "stopping existing Harbor instance ..." + docker-compose -f docker-compose.yml down -v + fi fi echo "" h2 "[Step $item]: starting Harbor ..." -docker-compose -f docker-compose*.yml up -d +if [ $with_notary ] +then + docker-compose -f docker-compose.yml -f docker-compose.notary.yml up -d +else + docker-compose -f docker-compose.yml up -d +fi protocol=http hostname=reg.mydomain.com diff --git a/make/jsminify.sh b/make/jsminify.sh deleted file mode 100755 index 37808d98c..000000000 --- a/make/jsminify.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -set -e -echo "This shell will minify the Javascript in Harbor project." -echo "Usage: #jsminify [src] [dest] [basedir]" - -#prepare workspace -rm -rf $2 /tmp/harbor.app.temp.js - -if [ -z $3 ] -then - BASEPATH=/go/bin -else - BASEPATH=$3 -fi - -#concat the js files from js include file -echo "Concat js files..." - -cat $1 | while read LINE || [[ -n $LINE ]] -do - if [ -n "$LINE" ] - then - TEMP="$BASEPATH""$LINE" - cat `echo "$TEMP" | sed 's/ + + + + + + + \ No newline at end of file diff --git a/src/ui/views/layout.htm b/src/ui/views/layout.htm deleted file mode 100644 index 430d7d2ce..000000000 --- a/src/ui/views/layout.htm +++ /dev/null @@ -1,28 +0,0 @@ - - - - - {{.HeaderInclude}} - {{.HeaderScriptInclude}} - {{.Title}} - - - {{.HeaderContent}} - {{.LayoutContent}} - {{.FooterContent}} - {{.FooterInclude}} - - \ No newline at end of file diff --git a/src/ui/views/mail.tpl b/src/ui/views/mail.tpl deleted file mode 100644 index 5db1d4293..000000000 --- a/src/ui/views/mail.tpl +++ /dev/null @@ -1,7 +0,0 @@ -From: {{.From}} -To: {{.To}} -Subject: {{.Subject}} -MIME-version: 1.0; -Content-Type: text/html; charset="UTF-8" - -{{.Message}} \ No newline at end of file diff --git a/src/ui/views/navigation-detail.htm b/src/ui/views/navigation-detail.htm deleted file mode 100644 index 9fc68cfad..000000000 --- a/src/ui/views/navigation-detail.htm +++ /dev/null @@ -1,22 +0,0 @@ - - \ No newline at end of file diff --git a/src/ui/views/navigation-header.htm b/src/ui/views/navigation-header.htm deleted file mode 100644 index 5a4e9bf08..000000000 --- a/src/ui/views/navigation-header.htm +++ /dev/null @@ -1,23 +0,0 @@ - -{{ if eq .HasLoggedIn true }} - -{{ end }} \ No newline at end of file diff --git a/src/ui/views/optional-menu.htm b/src/ui/views/optional-menu.htm deleted file mode 100644 index 56d78b9a1..000000000 --- a/src/ui/views/optional-menu.htm +++ /dev/null @@ -1,47 +0,0 @@ - -{{if eq .HasLoggedIn true }} - -{{ else }} - -{{ end }} \ No newline at end of file diff --git a/src/ui/views/project.htm b/src/ui/views/project.htm deleted file mode 100644 index 1e59e611f..000000000 --- a/src/ui/views/project.htm +++ /dev/null @@ -1,95 +0,0 @@ - - -
- -
-
-
- -
-
-
- - - - -
- {{ if eq .CanCreate true }} - - - {{ end }} -
-
- -
-
-
- - - - - - - - - -
// 'project_name' | tr //// 'repositories' | tr //// 'role' | tr //// 'creation_time' | tr //// 'publicity' | tr //// 'operation' | tr //
-
-
- - - - - - - - - - - - - - -

// 'no_projects_add_new_project' | tr //

//p.name////p.repo_count////vm.getProjectRole(p.current_user_role_id) | tr////p.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'// -    -
-
-
-
- -
-
-
-
diff --git a/src/ui/views/repository.htm b/src/ui/views/repository.htm deleted file mode 100644 index 7a9f94d45..000000000 --- a/src/ui/views/repository.htm +++ /dev/null @@ -1,46 +0,0 @@ - -
- -
-
-
-
- -
- - - - -
- - -
- - - - - -
-
-
-
-
-
\ No newline at end of file diff --git a/src/ui/views/reset-password-mail.tpl b/src/ui/views/reset-password-mail.tpl index 019512024..31fb438ef 100644 --- a/src/ui/views/reset-password-mail.tpl +++ b/src/ui/views/reset-password-mail.tpl @@ -15,7 +15,7 @@ -

{{.Hint}}:

+

Please click this link to reset your password:

{{.URL}}/reset_password?reset_uuid={{.UUID}} - \ No newline at end of file + diff --git a/src/ui/views/reset-password.htm b/src/ui/views/reset-password.htm deleted file mode 100644 index d0a29b7ce..000000000 --- a/src/ui/views/reset-password.htm +++ /dev/null @@ -1,64 +0,0 @@ - -
- -
-
-
-

// 'reset_password' | tr //

-
-
-
-
- -
- -
- // 'password_is_required' | tr // - // 'password_is_invalid' | tr // -
-

// 'password_desc' | tr //

-
-
- * -
-
-
- -
- -
- // 'password_does_not_match' | tr // -
-
-
- * -
-
-
-
- - -
-
-
- // vm.errorMessage // -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ui/views/search.htm b/src/ui/views/search.htm deleted file mode 100644 index 3daa16970..000000000 --- a/src/ui/views/search.htm +++ /dev/null @@ -1,40 +0,0 @@ - -
- -
-
-
-

// 'search_result' | tr //

-
-
- -
- -
- - -
-
-
-
-
-
\ No newline at end of file diff --git a/src/ui/views/sections/footer-content.htm b/src/ui/views/sections/footer-content.htm deleted file mode 100644 index 651a1e08e..000000000 --- a/src/ui/views/sections/footer-content.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/src/ui/views/sections/footer-include.htm b/src/ui/views/sections/footer-include.htm deleted file mode 100644 index e97a89e74..000000000 --- a/src/ui/views/sections/footer-include.htm +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/src/ui/views/sections/header-content.htm b/src/ui/views/sections/header-content.htm deleted file mode 100644 index f927847f9..000000000 --- a/src/ui/views/sections/header-content.htm +++ /dev/null @@ -1,39 +0,0 @@ - - \ No newline at end of file diff --git a/src/ui/views/sections/header-include.htm b/src/ui/views/sections/header-include.htm deleted file mode 100644 index 58f24a88b..000000000 --- a/src/ui/views/sections/header-include.htm +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{{ if eq .Lang "zh-CN" }} - -{{ else if eq .Lang "en-US" }} - -{{ end }} - diff --git a/src/ui/views/sections/script-include.htm b/src/ui/views/sections/script-include.htm deleted file mode 100644 index 7404a9d82..000000000 --- a/src/ui/views/sections/script-include.htm +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ui/views/sections/script-min-include.htm b/src/ui/views/sections/script-min-include.htm deleted file mode 100644 index 64f9960d6..000000000 --- a/src/ui/views/sections/script-min-include.htm +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/ui/views/sign-in.htm b/src/ui/views/sign-in.htm deleted file mode 100644 index 71b39fac6..000000000 --- a/src/ui/views/sign-in.htm +++ /dev/null @@ -1,57 +0,0 @@ - -{{ if eq .HasLoggedIn true }} -
-

// 'welcome' | tr //

- -

-

-
-{{ else }} -
-
-
- -
-
-
-
- -
- // vm.errorMessage | tr // -
-
-
-
-
-
- - {{ if and (eq .AuthMode "db_auth") (eq .SelfRegistration true) }} - - {{ end }} -
-
-
- {{ if eq .AuthMode "db_auth" }} - - {{ end }} -
-{{ end }} \ No newline at end of file diff --git a/src/ui/views/sign-up.htm b/src/ui/views/sign-up.htm deleted file mode 100644 index b5f29efe6..000000000 --- a/src/ui/views/sign-up.htm +++ /dev/null @@ -1,125 +0,0 @@ - -
- -
-
-
-

- {{ if eq .AddNew true }} - // 'add_new_title' | tr // - {{ else }} - // 'sign_up' | tr // - {{ end }} -

-
-
-
-
- -
- -
- // 'username_is_required' | tr // - // 'username_is_too_long' | tr // - // 'username_contains_illegal_chars' | tr // - // 'username_has_been_taken' | tr // -
-
-
- * -
-
-
- -
- -
- // 'email_is_required' | tr // - // 'email_content_illegal' | tr // - // 'email_has_been_taken' | tr // -
-

// 'email_desc' | tr //

-
-
- * -
-
-
- -
- -
- // 'full_name_is_required' | tr // - // 'full_name_contains_illegal_chars' | tr // - // 'full_name_is_too_long' | tr // -
-

// 'full_name_desc' | tr //

-
-
- * -
-
-
- -
- -
- // 'password_is_required' | tr // - // 'password_is_invalid' | tr // -
-

// 'password_desc' | tr //

-
-
- * -
-
-
- -
- -
- // 'password_does_not_match' | tr // -
-
-
- * -
-
-
- -
- -
- // 'comment_is_too_long' | tr // -
-
-
-
-
- {{ if eq .AddNew true }} - - {{ else }} - - {{ end }} -
-
-
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ui_ng/CODE_OF_CONDUCT.md b/src/ui_ng/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..dde656bd3 --- /dev/null +++ b/src/ui_ng/CODE_OF_CONDUCT.md @@ -0,0 +1,19 @@ +Contributor Code of Conduct +====================== + +As contributors and maintainers of the Clarity project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities. + +Communication through any of Clarity's channels (GitHub, mailing lists, Twitter, and so on) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. + +We promise to extend courtesy and respect to everyone involved in this project, regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Clarity project to do the same. + +If any member of the community violates this code of conduct, the maintainers of the Clarity project may take action, including removing issues, comments, and PRs or blocking accounts, as deemed appropriate. + +If you are subjected to or witness unacceptable behavior, or have any other concerns, please communicate with us. + +If you have suggestions to improve this Code of Conduct, please submit an issue or PR. + + +**Attribution** + +This Code of Conduct is adapted from the Angular project, version 0.3a-angular, available at this page: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md \ No newline at end of file diff --git a/src/ui_ng/CONTRIBUTING.md b/src/ui_ng/CONTRIBUTING.md new file mode 100644 index 000000000..15d09672d --- /dev/null +++ b/src/ui_ng/CONTRIBUTING.md @@ -0,0 +1,113 @@ + + +# Contributing to clarity-seed + +The clarity-seed project team welcomes contributions from the community. Follow the guidelines to contribute to the seed. + +## Contribution Guidelines + +Before you start working with Clarity, please complete the following steps: + +- Read our [code of conduct](/CODE_OF_CONDUCT.md). +- Read our [Developer Certificate of Origin](https://cla.vmware.com/dco). All contributions to this repository must be signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on as an open-source patch. + +## Contribution Flow + +Here are the typical steps in a contributor's workflow: + +- [Fork](https://help.github.com/articles/fork-a-repo/) the main Clarity seed repository. +- Clone your fork and set the upstream remote to the main Clarity repository. +- Set your name and e-mail in the Git configuration for signing. +- Create a topic branch from where you want to base your work. +- Make commits of logical units. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- [Submit a pull request](https://help.github.com/articles/about-pull-requests/). + +Example: + +``` shell +# Clone your forked repository +git clone git@github.com:/clarity-seed.git + +# Navigate to the directory +cd clarity-seed + +# Set name and e-mail configuration +git config user.name "John Doe" +git config user.email johndoe@example.com + +# Setup the upstream remote +git remote add upstream https://github.com/vmware/clarity-seed.git + +# Create a topic branch for your changes +git checkout -b my-new-feature master + +# After making the desired changes, commit and push to your fork +git commit -a -s +git push origin my-new-feature +``` + +### Staying In Sync With Upstream + +When your branch gets out of sync with the master branch, use the following to update: + +``` shell +git checkout my-new-feature +git fetch -a +git pull --rebase upstream master +git push --force-with-lease origin my-new-feature +``` + +### Updating Pull Requests + +If your PR fails to pass CI, or requires changes based on code review, you'll most likely want to squash these changes into existing commits. + +If your pull request contains a single commit, or your changes are related to the most recent commit, you can amend the commit. + +``` shell +git add . +git commit --amend +git push --force-with-lease origin my-new-feature +``` + +If you need to squash changes into an earlier commit, use the following: + +``` shell +git add . +git commit --fixup +git rebase -i --autosquash master +git push --force-with-lease origin my-new-feature +``` + +Make sure you add a comment to the PR indicating that your changes are ready to review. GitHub does not generate a notification when you use git push. + +### Formatting Commit Messages + +Use this format for your commit message: + +``` + + + + +Signed-off-by: Your Name +``` + +#### Writing Guidelines + +These documents provide guidance creating a well-crafted commit message: + + * [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/) + * [Closing Issues Via Commit Messages](https://help.github.com/articles/closing-issues-via-commit-messages/) + +## Reporting Bugs and Creating Issues + +You can submit an issue or a bug to our [GitHub repository](https://github.com/vmware/clarity-seed/issues). You must provide: + +* Instruction on how to replicate the issue +* The version number of Angular +* The version number of Clarity +* The version number of Node +* The browser name and version number +* The OS running the seed diff --git a/src/ui_ng/Clarity Seed_LICENSE_MIT.txt b/src/ui_ng/Clarity Seed_LICENSE_MIT.txt new file mode 100644 index 000000000..d336bce7b --- /dev/null +++ b/src/ui_ng/Clarity Seed_LICENSE_MIT.txt @@ -0,0 +1,16 @@ +Clarity Seed + +Copyright 2016 VMware, Inc. All rights reserved + +The MIT license (the License) set forth below applies to all parts of the Clarity Seed project. You may not use this file except in compliance with the License. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + diff --git a/src/ui_ng/Clarity Seed_NOTICE.txt b/src/ui_ng/Clarity Seed_NOTICE.txt new file mode 100644 index 000000000..7d47a3de7 --- /dev/null +++ b/src/ui_ng/Clarity Seed_NOTICE.txt @@ -0,0 +1,8 @@ +Clarity Seed + +Copyright (c) 2016 VMware, Inc. All Rights Reserved. + +This product is licensed to you under the MIT license (the "MIT License"). You may not use this product except in compliance with the MIT License. + +This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. + diff --git a/src/ui_ng/README.md b/src/ui_ng/README.md new file mode 100644 index 000000000..b7b51fa70 --- /dev/null +++ b/src/ui_ng/README.md @@ -0,0 +1,121 @@ +![Clarity](logo.png) + +Clarity Seed +============ +This is a seed project for Angular 2 applications using [Clarity](https://github.com/vmware/clarity). For more information on the Clarity Design System, visit the [Clarity website](https://vmware.github.io/clarity/). + +We offer this seed project in three different build systems: + +1. **Angular-CLI version (branch: master)** + +2. Webpack 2 version (branch: webpack) + +3. SystemJS version (branch: systemjs) + +Getting started +---------------------------------- + +#### Angular-CLI version + +This seed version provides the following out of the box: + +- Angular 2 application with [clarity-icons](https://www.npmjs.com/package/clarity-icons), [clarity-ui](https://www.npmjs.com/package/clarity-ui) and [clarity-angular](https://www.npmjs.com/package/clarity-angular) included +- Development and production builds +- Unit test setup with Jasmine and Karma +- End-to-end test setup with Protractor +- SASS processor +- TSLint +- And other goodies that come with [Angular-CLI](https://github.com/angular/angular-cli#generating-and-serving-an-angular2-project-via-a-development-server) (v1.0.0-beta.20-4) + +#### Installation +*Prerequisite*: Please install Angular-CLI by following [these instructions](https://github.com/angular/angular-cli#installation). +*Note*: Even though it's optional, we recommend you to use [yarn](https://yarnpkg.com/) instead of `npm install` for installing the dependencies. + +```bash +git clone https://github.com/vmware/clarity-seed.git +cd clarity-seed + +# install the project's dependencies +yarn # or run "npm install" + +# starts the application in dev mode and watches your files for livereload +ng serve +``` + +#### Using Angular-CLI +```bash +# generating a new component +ng g component my-new-component + +# generating a new directive +ng g directive my-new-directive + +# to learn more about Angular-CLI commands and their usages +ng help +``` + +For comprehensive documentation on Angular-CLI, please see their [github repository](https://github.com/angular/angular-cli). + +#### Test and build scripts + +```bash +# running unit tests +ng test + +# running e2e tests +ng e2e + +# dev build +ng build + +# prod build +ng build --prod +``` + +## Documentation + + +For documentation on the Clarity Design System, including a list of components and example usage, see [our website](https://vmware.github.io/clarity). + + +#### Directory structure +``` +. +├── README.md + +├── karma.conf.js <- configuration of the test runner +├── package.json <- dependencies of the project +├── protractor.config.js <- e2e tests configuration +├── src/ <- source code of the application +│   ├── app/ +│   │   └── component/ +│   │   └── .component.html +│   │   └── .component.scss +│   │   └── .component.spec.ts +│   │   └── .component.ts +│   │   └── app.component.html +│   │   └── app.component.scss +│   │   └── app.component.ts +│   │   └── app.e2e-spec.js <- sample e2e spec file +│   │   └── app.module.ts +│   │   └── app.routing.ts +│   │   └── main.ts <- boostrap file for the angular app +│   └── index.html +├── angular-cli.json <- configuration of the angular-cli +├── tsconfig.json <- configuration of the typescript project +├── tslint.json <- sample configuration file for tslint +└── yarn.lock +``` + + +## Contributing + +The Clarity project team welcomes contributions from the community. For more detailed information, see [CONTRIBUTING.md](CONTRIBUTING.md). + +## License + +The clarity-seed project is licensed under the MIT license. + +## Feedback + +If you find a bug or want to request a new feature, please open a [GitHub issue](https://github.com/vmware/clarity-seed/issues). diff --git a/src/ui_ng/angular-cli.json b/src/ui_ng/angular-cli.json new file mode 100644 index 000000000..e68629169 --- /dev/null +++ b/src/ui_ng/angular-cli.json @@ -0,0 +1,65 @@ +{ + "project": { + "version": "1.0.0-beta.20-4", + "name": "clarity-seed" + }, + "apps": [{ + "root": "src", + "outDir": "dist", + "assets": [ + "images", + "favicon.ico" + ], + "index": "index.html", + "main": "main.ts", + "test": "test.ts", + "tsconfig": "tsconfig.json", + "prefix": "app", + "mobile": false, + "styles": [ + "../node_modules/clarity-icons/clarity-icons.min.css", + "../node_modules/clarity-ui/clarity-ui.min.css", + "styles.css" + ], + "scripts": [ + "../node_modules/core-js/client/shim.min.js", + "../node_modules/mutationobserver-shim/dist/mutationobserver.min.js", + "../node_modules/@webcomponents/custom-elements/custom-elements.min.js", + "../node_modules/clarity-icons/clarity-icons.min.js", + "../node_modules/web-animations-js/web-animations.min.js" + ], + "environmentSource": "environments/environment.ts", + "environments": { + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + }], + "addons": [], + "packages": [], + "e2e": { + "protractor": { + "config": "./protractor.config.js" + } + }, + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "scss", + "prefixInterfaces": false, + "inline": { + "style": false, + "template": false + }, + "spec": { + "class": false, + "component": true, + "directive": true, + "module": false, + "pipe": true, + "service": true + } + } +} \ No newline at end of file diff --git a/src/ui_ng/e2e/app.e2e-spec.ts b/src/ui_ng/e2e/app.e2e-spec.ts new file mode 100644 index 000000000..fd0e8d0b9 --- /dev/null +++ b/src/ui_ng/e2e/app.e2e-spec.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import {ClaritySeedAppHome} from './app.po'; + +fdescribe('clarity-seed app', function () { + + let expectedMsg: string = 'This is a Clarity seed application. This is the default page that loads for the application.'; + + let page: ClaritySeedAppHome; + + beforeEach(() => { + page = new ClaritySeedAppHome(); + }); + + it('should display: ' + expectedMsg, () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual(expectedMsg) + }); +}); diff --git a/src/ui_ng/e2e/app.po.ts b/src/ui_ng/e2e/app.po.ts new file mode 100644 index 000000000..dbebbc34d --- /dev/null +++ b/src/ui_ng/e2e/app.po.ts @@ -0,0 +1,26 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { browser, element, by } from 'protractor'; + + +export class ClaritySeedAppHome { + + navigateTo() { + return browser.get('/'); + } + + getParagraphText() { + return element(by.css('my-app p')).getText(); + } +} diff --git a/src/ui_ng/e2e/tsconfig.json b/src/ui_ng/e2e/tsconfig.json new file mode 100644 index 000000000..dbb7b36d9 --- /dev/null +++ b/src/ui_ng/e2e/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "rootDir": "../", + "baseUrl": "", + "declaration": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "commonjs", + "moduleResolution": "node", + "outDir": "dist/out-tsc-e2e", + "sourceMap": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "types": [ + "jasmine" + ] + }, + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/src/ui_ng/favicon.ico b/src/ui_ng/favicon.ico new file mode 100644 index 000000000..8b3d01243 Binary files /dev/null and b/src/ui_ng/favicon.ico differ diff --git a/src/ui_ng/karma.conf.js b/src/ui_ng/karma.conf.js new file mode 100644 index 000000000..c96997aef --- /dev/null +++ b/src/ui_ng/karma.conf.js @@ -0,0 +1,44 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/0.13/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', 'angular-cli'], + plugins: [ + require('karma-jasmine'), + require('karma-phantomjs-launcher'), + require('karma-mocha-reporter'), + require('karma-remap-istanbul'), + require('angular-cli/plugins/karma') + ], + files: [ + {pattern: './src/test.ts', watched: false} + ], + preprocessors: { + './src/test.ts': ['angular-cli'] + }, + mime: { + 'text/x-typescript': ['ts', 'tsx'] + }, + remapIstanbulReporter: { + reports: { + html: 'coverage', + lcovonly: './coverage/coverage.lcov' + } + }, + angularCli: { + config: './angular-cli.json', + environment: 'dev' + }, + reporters: config.angularCli && config.angularCli.codeCoverage + ? ['mocha', 'karma-remap-istanbul'] + : ['mocha'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['PhantomJS'], + singleRun: true + }); +}; diff --git a/src/ui_ng/lib/AUTHORS b/src/ui_ng/lib/AUTHORS new file mode 100644 index 000000000..8038c3a69 --- /dev/null +++ b/src/ui_ng/lib/AUTHORS @@ -0,0 +1,3 @@ +# This file lists all individuals having contributed content to the repository. + +Steven zou diff --git a/src/ui_ng/lib/LICENSE b/src/ui_ng/lib/LICENSE new file mode 100644 index 000000000..ba316b7ba --- /dev/null +++ b/src/ui_ng/lib/LICENSE @@ -0,0 +1,4122 @@ +LICENSE + +Harbor UI component library version 0.1.0 + +Copyright 2016 VMware, Inc. All rights reserved. + +This product is licensed to you under the Apache License version 2.0 (the License). You may not use this product except in compliance with the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +====================================================================== + +Harbor version 0.5.0 includes a number of components with separate copyright notices and license terms. This product does not necessarily use all the open source components referred to below. Your use of the source code for these components is subject to the terms and conditions of the following licenses. + +======================== TABLE OF CONTENTS ============================ + +The following is a listing of the open source components detailed in +this document. This list is provided for your convenience; please read +further if you wish to review the copyright notice(s) and the full text +of the license associated with each component. + + +SECTION 1: BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES + + >>> alembic-0.8.8 + >>> angular-cookies-1.5.3 + >>> angular-messages-1.5.3 + >>> angularjs-1.5.3 + >>> bootstrap-3.3.6 + >>> bootstrap-datepicker-4.17.37 + >>> cronie-1.5.0 + >>> jquery-1.11.2 + >>> mattn/go-sqlite3-3fb7a0e7 + >>> moment-2.11.0 + >>> mqu/openldap-0.2 + >>> nginx-1.11.5 + >>> shadow-4.2.1 + + + +SECTION 2: Apache License, V2.0 + + >>> apr-util-1.5.4 + >>> beego-1.6.1 + >>> docker-distribution-2.5.0 + >>> docker-libtrust -9cbd2a1 + >>> registry-2.5.0 + + + +SECTION 3: GNU General Public License, V2.0 + + >>> logrotate-3.9.1 + >>> mysql-5.6 + + + +SECTION 4: GNU General Public License, V3.0 + + >>> rsyslog-8.15.0 + + + +SECTION 5: Mozilla Public License, V2.0 + + >>> go-sql-driver/mysql-1.2 + + + +APPENDIX. Standard License Files + + >>> Mozilla Public License, V2.0 + + >>> Apache License, V2.0 + + >>> GNU General Public License, V3.0 + + >>> GNU General Public License, V2.0 + + >>> GNU Library General Public License, V2.0 + + >>> GNU Lesser General Public License, V3.0 + + >>> Creative Commons Attribution License, V3.0 + + >>> Common Development and Distribution License, V1.1 + + + +--------------- SECTION 1: BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES ---------- + +BSD-STYLE, MIT-STYLE, OR SIMILAR STYLE LICENSES are applicable to the following component(s). + + +>>> alembic-0.8.8 + +This is the MIT license: http://www.opensource.org/licenses/mit-license.php + +Copyright (C) 2009-2016 by Michael Bayer. +Alembic is a trademark of Michael Bayer. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +>>> angular-cookies-1.5.3 + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +>>> angular-messages-1.5.3 + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +>>> angularjs-1.5.3 + +The MIT License + +Copyright (c) 2010-2015 Google, Inc. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +>>> bootstrap-3.3.6 + +The MIT License (MIT) + +Copyright (c) 2011-2015 Twitter, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +ADDITIONAL LICENSE INFORMATION: + +> CC- Attribution 3.0 Unported License + +[NOTE: VMware does not distribute the "docs\assets\css\src\docs.css" subpackage.] + +bootstrap-3.3.6\docs\assets\css \src\docs.css + +Bootstrap Docs (http://getbootstrap.com) +Copyright 2011-2015 Twitter, Inc. +Licensed under the Creative Commons Attribution 3.0 Unported License. For +details, see https://creativecommons.org/licenses/by/3.0/. + + +> MIT + +[PLEASE NOTE: VMWARE, INC. ELECTS TO USE AND DISTRIBUTE THIS COMPONENT UNDER THE TERMS OF THE MIT LICENSE. THE ORIGINAL LICENSE TERMS ARE REPRODUCED BELOW ONLY AS A REFERENCE.] + +bootstrap-3.3.6.tar.gz\bootstrap-3.3.6.tar\bootstrap-3.3.6\docs\assets\js\vendor\ jszip.min.js + +(c) 2009-2014 Stuart Knightley +Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. + + +> Apache 2.0 + +bootstrap-3.3.6.tar.gz\bootstrap-3.3.6.tar\bootstrap-3.3.6\docs\assets\js\vendor\ less.min.js + +Copyright (c) 2009-2014, Alexis Sellier +Licensed under the Apache v2 License. + +License for GLYPHICONS Halflings in Bootstrap + +GLYPHICONS Halflings font is also released as an extension of a Bootstrap www.getbootstrap.com for free and it is released under the same license as Bootstrap. While you are not required to include attribution on your Bootstrap-based projects, I would certainly appreciate any form of support, even a nice Tweet is enough. Of course if you want, you can say thank you and support me by buying more icons on GLYPHICONS.com. + + +>>> bootstrap-datepicker-4.17.37 + +The MIT License (MIT) + +Copyright (c) 2015 Jonathan Peterson (@Eonasdan) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +>>> cronie-1.5.0 + +Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") +Copyright (c) 1997,2000 by Internet Software Consortium, Inc. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Copyright (c) 1988, 1993, 1994 +The Regents of the University of California. All rights reserved. + +This code is derived from software written by Ken Arnold and +published in UNIX Review, Vol. 6, No. 8. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +Copyright (c) 1989, 1993 +The Regents of the University of California. All rights reserved. + +This code is derived from software contributed to Berkeley by +Paul Vixie. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +@(#)bitstring.h 8.1 (Berkeley) 7/19/93 + +ADDITIONAL LICENSE INFORMATION: + +> GPL 2.0 + +cronie-1.5.0.tar.gz\cronie-1.5.0.tar\cronie-1.5.0\anacron\global.h + + Anacron - run commands periodically + Copyright (C) 1998 Itai Tzur + Copyright (C) 1999 Sean 'Shaleh' Perry + Copyright (C) 2004 Pascal Hakim + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + The GNU General Public License can also be found in the file + `COPYING' that comes with the Anacron source distribution. + + +>>> jquery-1.11.2 + +Copyright 2014 jQuery Foundation and other contributors +http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +>>> mattn/go-sqlite3-3fb7a0e7 + +The MIT License (MIT) + +Copyright (c) 2014 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +ADDITIONAL LICENSE INFORMATION: + +> BSD-2 Clause + +go-sqlite3-master.zip\go-sqlite3-master\_example\mod_vtable\picojson.h + +Copyright 2009-2010 Cybozu Labs, Inc. +Copyright 2011 Kazuho Oku + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY CYBOZU LABS, INC. ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL CYBOZU LABS, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are +those of the authors and should not be interpreted as representing official +policies, either expressed or implied, of Cybozu Labs, Inc. + +> Public Domain + +go-sqlite3-master.zip\go-sqlite3-master\sqlite3ext.h + +2006 June 7 + +The author disclaims copyright to this source code. In place of +a legal notice, here is a blessing: + +May you do good and not evil. +May you find forgiveness for yourself and forgive others. +May you share freely, never taking more than you give. + + +>>> moment-2.11.0 + +Copyright (c) 2011-2015 Tim Wood, Iskren Chernev, Moment.js contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + +>>> mqu/openldap-0.2 + +/This work is part of OpenLDAP Software . + +Copyright 1998-2012 The OpenLDAP Foundation. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in file LICENSE in the +top-level directory of the distribution or, alternatively, at +. +/ +/ Portions Copyright (c) 1990 Regents of the University of Michigan. +All rights reserved. + +Redistribution and use in source and binary forms are permitted +provided that this notice is preserved and that due credit is given +to the University of Michigan at Ann Arbor. The name of the University +may not be used to endorse or promote products derived from this +software without specific prior written permission. This software +is provided ``as is'' without express or implied warranty. + + +>>> nginx-1.11.5 + +Copyright (C) 2002-2016 Igor Sysoev +Copyright (C) 2011-2016 Nginx, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + +>>> shadow-4.2.1 + +Copyright (c) 1991 - 1994, Julianne Frances Haugh +Copyright (c) 1996 - 2000, Marek Michalkiewicz +Copyright (c) 2000 - 2006, Tomasz Kloczko +Copyright (c) 2007 - 2011, Nicolas Franois +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1 Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer +2 Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution +3 The name of the copyright holders or contributors may not be used to +endorse or promote products derived from this software without +specific prior written permission + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED IN NO EVENT SHALL THE COPYRIGHT +HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + +ADDITIONAL LICENSE INFORMATION: + +> GPL 2.0 + +shadow-421tarxz\shadow-421tar\shadow-421\src\vipwc + +Copyright (c) 1997 , Guy Maor +Copyright (c) 1999 - 2000, Marek Michalkiewicz +Copyright (c) 2002 - 2006, Tomasz Kloczko +Copyright (c) 2007 - 2013, Nicolas Franois +All rights reserved + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version + +This program 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 +General Public License for more details + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA + +udbachktgz + +> BSD Style + +shadow-421tarxz\shadow-421tar\shadow-421\src\login_nopamc + +Copyright 1995 by Wietse Venema All rights reserved Individual files +may be covered by other copyrights (as noted in the file itself) + +This material was originally written and compiled by Wietse Venema at +Eindhoven University of Technology, The Netherlands, in 1990, 1991, +1992, 1993, 1994 and 1995 + +Redistribution and use in source and binary forms are permitted +provided that this entire copyright notice is duplicated in all such +copies + +This software is provided "as is" and without any expressed or implied +warranties, including, without limitation, the implied warranties of +merchantibility and fitness for any particular purpose + +> Public Domain + +shadow-421tarxz\shadow-421tar\shadow-421\man\zh_TW\man1\newgrp1 + +Original author unknown This man page is in the public domain +Modified Sat Oct 9 17:46:48 1993 by faith@csuncedu +TH NEWGRP 1 "9 October 1993" "Linux 12" "Linux Programmer's Manual" + +> GPL 3.0 with Bison Parser Exception + +shadow-421tarxz\shadow-421tar\shadow-421\libmisc\getdatec + +A Bison parser, made by GNU Bison 302 + +Bison implementation for Yacc-like parsers in C + +Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version + +This program 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 General Public License for more details + +You should have received a copy of the GNU General Public License +along with this program If not, see + +As a special exception, you may create a larger work that contains +part or all of the Bison parser skeleton and distribute that work +under terms of your choice, so long as that work isn't itself a +parser generator using the skeleton or a modified version thereof +as a parser skeleton Alternatively, if you modify or redistribute +the parser skeleton itself, you may (at your option) remove this +special exception, which will cause the skeleton and the resulting +Bison output files to be licensed under the GNU General Public +License without this special exception + +This special exception was added by the Free Software Foundation in +version 22 of Bison + +> GPL 3.0 + +shadow-421tarxz\shadow-421tar\shadow-421\man\tr\man1\chfn.1 + +chfn.1 -- change your finger information +(c) 1994 by salvatore valente +this program is free software. you can redistribute it and +modify it under the terms of the gnu general public license. +there is no warranty. + +> MIT-Style + +shadow-421tarxz\shadow-421tar\shadow-421\aclocalm4 + +Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, +Inc +This file is free software; the Free Software Foundation +gives unlimited permission to copy and/or distribute it, +with or without modifications, as long as this notice is preserved + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, to the extent permitted by law; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE + +> BSD 3-Clause + +[PLEASE NOTE: VMWARE, INC ELECTS TO USE AND DISTRIBUTE THIS COMPONENT UNDER THE TERMS OF THE BSD LICENSE THE ORIGINAL LICENSE TERMS ARE REPRODUCED BELOW ONLY AS A REFERENCE] + +shadow-421tarxz\shadow-421tar\shadow-421\man\tr\man1\passwd1 + +Copyright Red Hat, Inc, 1998, 1999, 2002 + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1 Redistributions of source code must retain the above copyright +notice, and the entire permission notice in its entirety, +including the disclaimer of warranties +2 Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution +3 The name of the author may not be used to endorse or promote +products derived from this software without specific prior +written permission + +ALTERNATIVELY, this product may be distributed under the terms of +the GNU Public License, in which case the provisions of the GPL are +required INSTEAD OF the above restrictions (This clause is +necessary due to a potential bad interaction between the GPL and +the restrictions contained in a BSD-style copyright) + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE + +Copyright (c) Cristian Gafton, 1998, + +> LDP General Public License + +shadow-421tarxz\shadow-421tar\shadow-421\man\hu\man1\su1 + +You may copy, distribute and modify under the terms of the LDP General +Public License as specified in the LICENSE file that comes with the +gnumaniak distribution + +The author kindly requests that no comments regarding the "better" +suitability or up\-to\-date notices of any info documentation alternative +is added without contacting him first + +(C) 1999 Ragnar Hojland Espinosa + +GNU su man page +man pages are NOT obsolete! + +TH su 1 "18 August 1999" "GNU Shell Utilities 20" + + +--------------- SECTION 2: Apache License, V2.0 ---------- + +Apache License, V2.0 is applicable to the following component(s). + + +>>> apr-util-1.5.4 + +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +ADDITIONAL LICENSE INFORMATION: + +> apr-util-1.5.4.tar.gz\apr-util-1.5.4.tar\apr-util-1.5.4\NOTICE + +Apache Portable Runtime Utility Library +Copyright (c) 2000-2014 The Apache Software Foundation. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were developed at the National Center +for Supercomputing Applications (NCSA) at the University of +Illinois at Urbana-Champaign. + +This software contains code derived from the RSA Data Security +Inc. MD5 Message-Digest Algorithm, including various +modifications by Spyglass Inc., Carnegie Mellon University, and +Bell Communications Research, Inc (Bellcore). + +> RSA Data Security + +apr-util-1.5.4.tar.gz\apr-util-1.5.4.tar\apr-util-1.5.4\include\apr_md4.h + +This is derived from material copyright RSA Data Security, Inc. +Their notice is reproduced below in its entirety. + +Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD4 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD4 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + +> MIT + +apr-util-1.5.4.tar.gz\apr-util-1.5.4.tar\apr-util-1.5.4\xml\expat\COPYING + +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd +and Clark Cooper +Copyright (c) 2001, 2002 Expat maintainers. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +> OpenLDAP Public License (BSD-Style) + +Portions Copyright 1998-2002 The OpenLDAP Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. A copy of this license is available at +http://www.OpenLDAP.org/license.html or in file LICENSE in the +top-level directory of the distribution. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Individual files and/or contributed packages may be copyright by +other parties and subject to additional restrictions. + +This work is derived from the University of Michigan LDAP v3.3 +distribution. Information concerning this software is available +at: http://www.umich.edu/~dirsvcs/ldap/ + +This work also contains materials derived from public sources. + +Additional information about OpenLDAP can be obtained at: +http://www.openldap.org/ + + + +Portions Copyright (c) 1992-1996 Regents of the University of Michigan. +All rights reserved. + +Redistribution and use in source and binary forms are permitted +provided that this notice is preserved and that due credit is given +to the University of Michigan at Ann Arbor. The name of the University +may not be used to endorse or promote products derived from this +software without specific prior written permission. This software +is provided ``as is'' without express or implied warranty. + + +>>> beego-1.6.1 + +Copyright 2014 beego Author. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +ADDITIONAL LICENSE INFORMATION: + +> BSD + +beego-1.6.1.tar.gz\beego-1.6.1.tar\beego-1.6.1\session\README.md + +LICENSE + +BSD License http://creativecommons.org/licenses/BSD/ + +> MIT + +beego-1.6.1.tar.gz\beego-1.6.1.tar\beego-1.6.1\utils\captcha\LICENSE + +Copyright (c) 2011-2014 Dmitry Chestnykh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +>>> docker-distribution-2.5.0 + +License + +This project is distributed under [Apache License, Version 2.0](LICENSE). + +ADDITIONAL LICENSE INFORMATION: + +> MIT + +distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\bugsnag\bugsnag-go\LICENSE.txt + +Copyright (c) 2014 Bugsnag + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +> MIT Style + +distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\bugsnag\osext\LICENSE + +Copyright (c) 2012 Daniel Theophanes + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +> CC-Attribution 3.0 + +distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\docker\libtrust\README.md + +Copyright and license + +Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. +Docs released under Creative commons. + + +> BSD 3-Clause + +distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\golang\protobuf\LICENSE + +Go support for Protocol Buffers - Google's data interchange format + +Copyright 2010 The Go Authors. All rights reserved. +https://github.com/golang/protobuf + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +* Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +> BSD 2-Clause + +distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\github.com\gorilla\handlers\LICENSE + +Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +> BSD-3 + +distribution-2.5.0.tar.gz\distribution-2.5.0.tar\distribution-2.5.0\vendor\golang.org\x\crypto\LICENSE + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. +* Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +>>> docker-libtrust -9cbd2a1 + +Copyright and license + +Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. +Docs released under Creative commons. + + +>>> registry-2.5.0 + +License: Apache 2.0 + + +--------------- SECTION 3: GNU General Public License, V2.0 ---------- + +GNU General Public License, V2.0 is applicable to the following component(s). + + +>>> logrotate-3.9.1 + +LICENSE : GPL 2.0 + +ADDITIONAL LICENSE INFORMATION: + +> BSD 3-Clause + +logrotate-3.9.1.tar.gz\logrotate-3.9.1.tar\logrotate-3.9.1\queue.h + +Copyright (c) 1991, 1993 + The Regents of the University of California. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + +>>> mysql-5.6 + +License: GPL 2.0 + + +--------------- SECTION 4: GNU General Public License, V3.0 ---------- + +GNU General Public License, V3.0 is applicable to the following component(s). + + +>>> rsyslog-8.15.0 + +Copyright 2007, 2014 Rainer Gerhards and Adiscon GmbH. + +This file is part of rsyslog. + +Rsyslog is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Rsyslog 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Rsyslog. If not, see . + +A copy of the GPL can be found in the file "COPYING" in this distribution. + +ADDITIONAL LICENSE INFORMATION: + +> CDDL 1.1 + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\compat\getifaddrs.c + +The contents of this file are subject to the terms of the +Common Development and Distribution License (the "License"). +You may not use this file except in compliance with the License. + +You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +or http://www.opensolaris.org/os/licensing. +See the License for the specific language governing permissions +and limitations under the License. + +When distributing Covered Code, include this CDDL HEADER in each +file and include the License file at usr/src/OPENSOLARIS.LICENSE. +If applicable, add the following below this CDDL HEADER, with the +fields enclosed by brackets "[]" replaced with your own identifying +information: Portions Copyright [yyyy] [name of copyright owner] + +> Apache 2.0 + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\compat\strndup.c + +Copyright 2015 Rainer Gerhards and Adiscon + +This file is part of rsyslog. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 +-or- +see COPYING.ASL20 in the source distribution + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +> LGPL 3.0 + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\contrib\imzmq3\imzmq3.c + +Copyright 2012 Talksum, Inc. + +This program is free software: you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public License +as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +This program 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this program. If not, see +. + +Authors: +David Kelly +Hongfei Cheng hongfeic@talksum.com + +> GPL 3.0 (WITH BISON PARSER EXCEPTION) + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\grammar\grammar.c + +Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +As a special exception, you may create a larger work that contains +part or all of the Bison parser skeleton and distribute that work +under terms of your choice, so long as that work isn't itself a +parser generator using the skeleton or a modified version thereof +as a parser skeleton. Alternatively, if you modify or redistribute +the parser skeleton itself, you may (at your option) remove this +special exception, which will cause the skeleton and the resulting +Bison output files to be licensed under the GNU General Public +License without this special exception. + +This special exception was added by the Free Software Foundation in +version 2.2 of Bison. + +> MIT + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\plugins\omelasticsearch\cJSON\cjson.c + +Copyright (c) 2009 Dave Gamble + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +> BSD 3-Clause + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\runtime\hashtable.h + +Copyright (c) 2002, Christopher Clark +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +Neither the name of the original author; nor the names of any contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +> GPL 2.0 + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\tools\rsyslog.conf.5 + +This file is part of the rsyslog package, an enhanced system log daemon. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + +> LGPL 2.0 + +rsyslog-8.15.0.tar.gz\rsyslog-8.15.0.tar\rsyslog-8.15.0\tools\rscryutil.rst + +COPYRIGHT +========= + +This page is part of the *rsyslog* project, and is available under +LGPLv2. + + +--------------- SECTION 5: Mozilla Public License, V2.0 ---------- + +Mozilla Public License, V2.0 is applicable to the following component(s). + + +>>> go-sql-driver/mysql-1.2 + +Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this file, +You can obtain one at http://mozilla.org/MPL/2.0/. + + +=============== APPENDIX. Standard License Files ============== + + + +--------------- SECTION 1: Mozilla Public License, V2.0 ----------- + +Mozilla Public License +Version 2.0 + +1. Definitions + +1.1. Contributor +means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. + +1.2. Contributor Version +means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributors Contribution. + +1.3. Contribution +means Covered Software of a particular Contributor. + +1.4. Covered Software +means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. + +1.5. Incompatible With Secondary Licenses +means + +that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or + +that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. + +1.6. Executable Form +means any form of the work other than Source Code Form. + +1.7. Larger Work +means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. + +1.8. License +means this document. + +1.9. Licensable +means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. + +1.10. Modifications +means any of the following: + +any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or + +any new file in Source Code Form that contains any Covered Software. + +1.11. Patent Claims of a Contributor +means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. + +1.12. Secondary License +means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. + +1.13. Source Code Form +means the form of the work preferred for making modifications. + +1.14. You (or Your) +means an individual or a legal entity exercising rights under this License. For legal entities, You includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, control means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. + +2. License Grants and Conditions + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: + +under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and + +under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: + +for any code that a Contributor has removed from Covered Software; or + +for infringements caused by: (i) Your and any other third partys modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or + +under Patent Claims infringed by Covered Software in the absence of its Contributions. + +This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. + +3. Responsibilities + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients rights in the Source Code Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and + +You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + +If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. + +6. Disclaimer of Warranty + +Covered Software is provided under this License on an as is basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer. + +7. Limitation of Liability + +Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such partys negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + +Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a partys ability to bring cross-claims or counter-claims. + +9. Miscellaneous + +This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. + +10. Versions of the License + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + +If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + +This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - Incompatible With Secondary Licenses Notice + +This Source Code Form is Incompatible With Secondary Licenses, as defined by the Mozilla Public License, v. 2.0. + +--------------- SECTION 2: Apache License, V2.0 ----------- + +Apache License + +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the +copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other +entities that control, are controlled by, or are under common control +with that entity. For the purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or management +of such entity, whether by contract or otherwise, or (ii) ownership +of fifty percent (50%) or more of the outstanding shares, or (iii) +beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled +object code, generated documentation, and conversions to other media +types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a copyright +notice that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this +License, Derivative Works shall not include works that remain separable +from, or merely link (or bind by name) to the interfaces of, the Work +and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the +original version of the Work and any modifications or additions to +that Work or Derivative Works thereof, that is intentionally submitted +to Licensor for inclusion in the Work by the copyright owner or by an +individual or Legal Entity authorized to submit on behalf of the copyright +owner. For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or its +representatives, including but not limited to communication on electronic +mailing lists, source code control systems, and issue tracking systems +that are managed by, or on behalf of, the Licensor for the purpose of +discussing and improving the Work, but excluding communication that is +conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. +Subject to the terms and conditions of this License, each Contributor +hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, +royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and +distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. +Subject to the terms and conditions of this License, each Contributor +hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, +royalty- free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and +otherwise transfer the Work, where such license applies only to those +patent claims licensable by such Contributor that are necessarily +infringed by their Contribution(s) alone or by combination of +their Contribution(s) with the Work to which such Contribution(s) +was submitted. If You institute patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted +to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. +You may reproduce and distribute copies of the Work or Derivative Works +thereof in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + a. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + + b. You must cause any modified files to carry prominent notices stating + that You changed the files; and + + c. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + + d. If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one of + the following places: within a NOTICE text file distributed as part + of the Derivative Works; within the Source form or documentation, + if provided along with the Derivative Works; or, within a display + generated by the Derivative Works, if and wherever such third-party + notices normally appear. The contents of the NOTICE file are for + informational purposes only and do not modify the License. You + may add Your own attribution notices within Derivative Works that + You distribute, alongside or as an addendum to the NOTICE text + from the Work, provided that such additional attribution notices + cannot be construed as modifying the License. You may add Your own + copyright statement to Your modifications and may provide additional + or different license terms and conditions for use, reproduction, or + distribution of Your modifications, or for any such Derivative Works + as a whole, provided Your use, reproduction, and distribution of the + Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. +Unless You explicitly state otherwise, any Contribution intentionally +submitted for inclusion in the Work by You to the Licensor shall be +under the terms and conditions of this License, without any additional +terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may +have executed with Licensor regarding such Contributions. + +6. Trademarks. +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. +Unless required by applicable law or agreed to in writing, Licensor +provides the Work (and each Contributor provides its Contributions) on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +express or implied, including, without limitation, any warranties or +conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. +In no event and under no legal theory, whether in tort (including +negligence), contract, or otherwise, unless required by applicable law +(such as deliberate and grossly negligent acts) or agreed to in writing, +shall any Contributor be liable to You for damages, including any direct, +indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to +use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other +commercial damages or losses), even if such Contributor has been advised +of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. +While redistributing the Work or Derivative Works thereof, You may +choose to offer, and charge a fee for, acceptance of support, warranty, +indemnity, or other liability obligations and/or rights consistent with +this License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf of +any other Contributor, and only if You agree to indemnify, defend, and +hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such +warranty or additional liability. + +END OF TERMS AND CONDITIONS + + + +--------------- SECTION 3: GNU General Public License, V3.0 ----------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + + +--------------- SECTION 4: GNU General Public License, V2.0 ----------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + + +--------------- SECTION 5: GNU Library General Public License, V2.0 ----------- + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + +--------------- SECTION 6: GNU Lesser General Public License, V3.0 ----------- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + + +--------------- SECTION 7: Creative Commons Attribution License, V3.0 ----------- + +Creative Commons Attribution 3.0 Unported + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL +SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT +RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" +BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION +PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN +AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE +MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work + and other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may + be recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered + an Adaptation for the purpose of this License. + + b. "Collection" means a collection of literary or artistic works, + such as encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed in + Section 1(f) below, which, by reason of the selection and arrangement + of their contents, constitute intellectual creations, in which the + Work is included in its entirety in unmodified form along with one or + more other contributions, each constituting separate and independent + works in themselves, which together are assembled into a collective + whole. A work that constitutes a Collection will not be considered + an Adaptation (as defined above) for the purposes of this License. + + c. "Distribute" means to make available to the public the original + and copies of the Work or Adaptation, as appropriate, through sale + or other transfer of ownership. + + d. "Licensor" means the individual, individuals, entity or entities + that offer(s) the Work under the terms of this License. + + e. "Original Author" means, in the case of a literary or artistic + work, the individual, individuals, entity or entities who created the + Work or if no individual or entity can be identified, the publisher; + and in addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, + the organization that transmits the broadcast. + + f. "Work" means the literary and/or artistic work offered under the + terms of this License including without limitation any production + in the literary, scientific and artistic domain, whatever may be + the mode or form of its expression including digital form, such + as a book, pamphlet and other writing; a lecture, address, sermon + or other work of the same nature; a dramatic or dramatico-musical + work; a choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving + or lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied art; + an illustration, map, plan, sketch or three-dimensional work relative + to geography, topography, architecture or science; a performance; + a broadcast; a phonogram; a compilation of data to the extent it is + protected as a copyrightable work; or a work performed by a variety + or circus performer to the extent it is not otherwise considered a + literary or artistic work. + + g. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License + with respect to the Work, or who has received express permission + from the Licensor to exercise rights under this License despite a + previous violation. + + h. "Publicly Perform" means to perform public recitations of the + Work and to communicate to the public those public recitations, by + any means or process, including by wire or wireless means or public + digital performances; to make available to the public Works in such + a way that members of the public may access these Works from a place + and at a place individually chosen by them; to perform the Work to the + public by any means or process and the communication to the public of + the performances of the Work, including by public digital performance; + to broadcast and rebroadcast the Work by any means including signs, + sounds or images. + + i. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage + of a protected performance or phonogram in digital form or other + electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or + more Collections, and to Reproduce the Work as incorporated in the + Collections; + + b. to create and Reproduce Adaptations provided that any such + Adaptation, including any translation in any medium, takes reasonable + steps to clearly label, demarcate or otherwise identify that changes + were made to the original Work. For example, a translation could be + marked "The original work was translated from English to Spanish," or + a modification could indicate "The original work has been modified."; + + c. to Distribute and Publicly Perform the Work including as + incorporated in Collections; and, + + d. to Distribute and Publicly Perform Adaptations. + + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions + in which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor reserves + the exclusive right to collect such royalties for any exercise by + You of the rights granted under this License; + + ii. Waivable Compulsory License Schemes. In those jurisdictions + in which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor waives the + exclusive right to collect such royalties for any exercise by You + of the rights granted under this License; and, + + iii. Voluntary License Schemes. The Licensor waives the right + to collect royalties, whether individually or, in the event that + the Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License. The above rights + may be exercised in all media and formats whether now known or + hereafter devised. The above rights include the right to make such + modifications as are technically necessary to exercise the rights + in other media and formats. Subject to Section 8(f), all rights + not expressly granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly +made subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the + terms of this License. You must include a copy of, or the Uniform + Resource Identifier (URI) for, this License with every copy of the + Work You Distribute or Publicly Perform. You may not offer or impose + any terms on the Work that restrict the terms of this License or the + ability of the recipient of the Work to exercise the rights granted to + that recipient under the terms of the License. You may not sublicense + the Work. You must keep intact all notices that refer to this License + and to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly Perform + the Work, You may not impose any effective technological measures on + the Work that restrict the ability of a recipient of the Work from + You to exercise the rights granted to that recipient under the terms + of the License. This Section 4(a) applies to the Work as incorporated + in a Collection, but this does not require the Collection apart from + the Work itself to be made subject to the terms of this License. If + You create a Collection, upon notice from any Licensor You must, + to the extent practicable, remove from the Collection any credit as + required by Section 4(b), as requested. If You create an Adaptation, + upon notice from any Licensor You must, to the extent practicable, + remove from the Adaptation any credit as required by Section 4(b), + as requested. + + b. If You Distribute, or Publicly Perform the Work or any Adaptations + or Collections, You must, unless a request has been made pursuant + to Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) + for attribution ("Attribution Parties") in Licensor's copyright + notice, terms of service or by other reasonable means, the name of + such party or parties; (ii) the title of the Work if supplied; (iii) + to the extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and (iv) , consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required + by this Section 4 (b) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of + these credits and in a manner at least as prominent as the credits + for the other contributing authors. For the avoidance of doubt, You + may only use the credit required by this Section for the purpose of + attribution in the manner set out above and, by exercising Your rights + under this License, You may not implicitly or explicitly assert or + imply any connection with, sponsorship or endorsement by the Original + Author, Licensor and/or Attribution Parties, as appropriate, of You + or Your use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution Parties. + + c. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute + or Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify + or take other derogatory action in relation to the Work which would + be prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION +OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. +EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR +BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, +CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS +LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this + License. Individuals or entities who have received Adaptations or + Collections from You under this License, however, will not have their + licenses terminated provided such individuals or entities remain in + full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 + will survive any termination of this License. + + b. Subject to the above terms and conditions, the license granted + here is perpetual (for the duration of the applicable copyright in + the Work). Notwithstanding the above, Licensor reserves the right to + release the Work under different license terms or to stop distributing + the Work at any time; provided, however that any such election will not + serve to withdraw this License (or any other license that has been, + or is required to be, granted under the terms of this License), and + this License will continue in full force and effect unless terminated + as stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a + Collection, the Licensor offers to the recipient a license to the + Work on the same terms and conditions as the license granted to You + under this License. + + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability + of the remainder of the terms of this License, and without further + action by the parties to this agreement, such provision shall be + reformed to the minimum extent necessary to make such provision valid + and enforceable. + + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + + e. This License constitutes the entire agreement between the parties + with respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and + the Universal Copyright Convention (as revised on July 24, 1971). These + rights and subject matter take effect in the relevant jurisdiction + in which the License terms are sought to be enforced according to + the corresponding provisions of the implementation of those treaty + provisions in the applicable national law. If the standard suite of + rights granted under applicable copyright law includes additional + rights not granted under this License, such additional rights are + deemed to be included in the License; this License is not intended + to restrict the license of any rights under applicable law. + + +--------------- SECTION 7: Common Development and Distribution License, V1.1 ----------- + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)Version 1.1 + +1. Definitions. + +1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications. + +1.2. "Contributor Version" means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. + +1.3. "Covered Software" means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. + +1.4. "Executable" means the Covered Software in any form other than Source Code. + +1.5. "Initial Developer" means the individual or entity that first makes Original Software available under this License. + +1.6. "Larger Work" means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. + +1.7. "License" means this document. + +1.8. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. + +1.9. "Modifications" means the Source Code and Executable form of any of the following: +A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; +B. Any new file that contains any part of the Original Software or previous Modification; or +C. Any new file that is contributed or otherwise made available under the terms of this License. + +1.10. "Original Software" means the Source Code and Executable form of computer software code that is originally released under this License. + +1.11. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. + +1.12. "Source Code" means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. + +1.13. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. + +2. License Grants. + +2.1. The Initial Developer Grant. +Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: +(a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and +(b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). +(c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. +(d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices. + +2.2. Contributor Grant. +Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: +(a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and +(b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). +(c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. +(d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. + +3. Distribution Obligations. + +3.1. Availability of Source Code. +Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. + +3.2. Modifications. +The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. + +3.3. Required Notices. +You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. + +3.4. Application of Additional Terms. +You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients' rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. + +3.5. Distribution of Executable Versions. +You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient's rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. + +3.6. Larger Works. +You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. + +4. Versions of the License. + +4.1. New Versions. +Oracle is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. + +4.2. Effect of New Versions. +You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. + +4.3. Modified Versions. +When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. +COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +6. TERMINATION. + +6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. + +6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as "Participant") alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. + +6.3. If You assert a patent infringement claim against Participant alleging that the Participant Software directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. + +6.4. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. +The Covered Software is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" (as that term is defined at 48 C.F.R. sec. 252.227-7014(a)(1)) and "commercial computer software documentation" as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. + +9. MISCELLANEOUS. +This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction's conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. +As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) +The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. + + + +=========================================================================== + +To the extent any open source components are licensed under the GPL +and/or LGPL, or other similar licenses that require the source code +and/or modifications to source code to be made available (as would be +noted above), you may obtain a copy of the source code corresponding to +the binaries for such open source components and modifications thereto, +if any, (the "Source Files"), by downloading the Source Files from +VMware's website at http://www.vmware.com/download/open_source.html, or +by sending a request, with your name and address to: VMware, Inc., 3401 +Hillview Avenue, Palo Alto, CA 94304, United States of America. All such +requests should clearly specify: OPEN SOURCE FILES REQUEST, Attention +General Counsel. VMware shall mail a copy of the Source Files to you on +a CD or equivalent physical medium. This offer to obtain a copy of the +Source Files is valid for three years from the date you acquired this +Software product. Alternatively, the Source Files may accompany the +VMware product. + +[HARBOR050GASR111116] \ No newline at end of file diff --git a/src/ui_ng/lib/README.md b/src/ui_ng/lib/README.md new file mode 100644 index 000000000..6e9c176e0 --- /dev/null +++ b/src/ui_ng/lib/README.md @@ -0,0 +1,2 @@ +# harbor-angular +For publishing Harbor shared UI components. diff --git a/src/ui_ng/lib/angular-cli.json b/src/ui_ng/lib/angular-cli.json new file mode 100644 index 000000000..6c2a4b6a1 --- /dev/null +++ b/src/ui_ng/lib/angular-cli.json @@ -0,0 +1,64 @@ +{ + "project": { + "version": "0.1.0", + "name": "harbor-ui" + }, + "apps": [{ + "root": ".", + "outDir": "../dist", + "assets": [ + "images", + "favicon.ico" + ], + "index": "index.html", + "main": "index.ts", + "test": "test.ts", + "tsconfig": "tsconfig.json", + "prefix": "", + "mobile": false, + "styles": [ + "node_modules/clarity-icons/clarity-icons.min.css", + "node_modules/clarity-ui/clarity-ui.min.css" + ], + "scripts": [ + "node_modules/core-js/client/shim.min.js", + "node_modules/mutationobserver-shim/dist/mutationobserver.min.js", + "node_modules/@webcomponents/custom-elements/custom-elements.min.js", + "node_modules/clarity-icons/clarity-icons.min.js", + "node_modules/web-animations-js/web-animations.min.js" + ], + "environmentSource": "environments/environment.ts", + "environments": { + "dev": "environments/environment.ts", + "prod": "environments/environment.prod.ts" + } + }], + "addons": [], + "packages": [], + "e2e": { + "protractor": { + "config": "./protractor.config.js" + } + }, + "test": { + "karma": { + "config": "./karma.conf.js" + } + }, + "defaults": { + "styleExt": "scss", + "prefixInterfaces": false, + "inline": { + "style": false, + "template": false + }, + "spec": { + "class": false, + "component": true, + "directive": true, + "module": false, + "pipe": true, + "service": true + } + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/index.ts b/src/ui_ng/lib/index.ts new file mode 100644 index 000000000..f3c80e03c --- /dev/null +++ b/src/ui_ng/lib/index.ts @@ -0,0 +1 @@ +export * from './src/index'; \ No newline at end of file diff --git a/src/ui_ng/lib/package.json b/src/ui_ng/lib/package.json new file mode 100644 index 000000000..3c3d45c38 --- /dev/null +++ b/src/ui_ng/lib/package.json @@ -0,0 +1,60 @@ +{ + "name": "harbor-ui", + "version": "0.1.0", + "description": "Harbor shared UI components based on Clarity and Angular4", + "scripts": { + "start": "ng serve --host 0.0.0.0 --port 4500 --proxy-config proxy.config.json", + "lint": "tslint \"src/**/*.ts\"", + "test": "ng test --single-run", + "pree2e": "webdriver-manager update", + "e2e": "protractor", + "cleanup": "rimraf dist/bundles dist/src dist/index.d.ts dist/index.metadata.json dist/index.js dist/index.js.map dist/LICENSE dist/AUTHORS", + "copy": "copyfiles -f LICENSE AUTHORS pkg/package.json dist", + "transpile": "ngc", + "package": "rollup -c", + "minify": "uglifyjs dist/bundles/harborui.umd.js --screw-ie8 --compress --mangle --comments --output dist/bundles/harborui.umd.min.js", + "build": "npm run cleanup && npm run transpile && npm run package && npm run minify && npm run copy" + }, + "private": true, + "dependencies": { + "@angular/common": "^4.0.0", + "@angular/compiler": "^4.0.0", + "@angular/core": "^4.0.0", + "@angular/http": "^4.0.0", + "clarity-angular": "^0.8.14", + "clarity-icons": "^0.8.14", + "clarity-ui": "^0.8.14", + "core-js": "^2.4.1", + "rxjs": "^5.0.1", + "ts-helpers": "^1.1.1", + "zone.js": "^0.7.2" + }, + "devDependencies": { + "@angular/cli": "^1.0.0", + "@angular/compiler-cli": "^2.4.1", + "@types/core-js": "^0.9.41", + "@types/jasmine": "~2.2.30", + "@types/node": "^6.0.42", + "bootstrap": "4.0.0-alpha.5", + "codelyzer": "~2.0.0-beta.4", + "enhanced-resolve": "^3.0.0", + "jasmine-core": "2.4.1", + "jasmine-spec-reporter": "2.5.0", + "karma": "1.2.0", + "karma-cli": "^1.0.1", + "karma-jasmine": "^1.0.2", + "karma-mocha-reporter": "^2.2.1", + "karma-phantomjs-launcher": "^1.0.0", + "karma-remap-istanbul": "^0.2.1", + "protractor": "^4.0.9", + "rollup": "^0.41.6", + "ts-node": "1.2.1", + "tslint": "^4.1.1", + "typescript": "~2.0.3", + "typings": "^1.4.0", + "uglify-js": "^2.8.22", + "webdriver-manager": "10.2.5", + "rimraf": "^2.6.1", + "copyfiles": "^1.2.0" + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/pkg/package.json b/src/ui_ng/lib/pkg/package.json new file mode 100644 index 000000000..124f78722 --- /dev/null +++ b/src/ui_ng/lib/pkg/package.json @@ -0,0 +1,34 @@ +{ + "name": "harbor-ui", + "version": "0.1.0", + "description": "Harbor shared UI components based on Clarity and Angular4", + "author": "Harbor", + "module": "index.js", + "main": "bundles/harborui.umd.min.js", + "jsnext:main": "index.js", + "typings": "index.d.ts", + "keywords": [ + "Harbor", + "Clarity", + "Angular4" + ], + "license": "Apache 2.0", + "repository": { + "type": "git", + "url": "https://github.com/vmware/harbor.git" + }, + "homepage": "https://github.com/vmware/harbor#readme", + "peerDependencies": { + "@angular/common": "^4.0.0", + "@angular/compiler": "^4.0.0", + "@angular/core": "^4.0.0", + "@angular/http": "^4.0.0", + "clarity-angular": "^0.8.14", + "clarity-icons": "^0.8.14", + "clarity-ui": "^0.8.14", + "core-js": "^2.4.1", + "rxjs": "^5.0.1", + "ts-helpers": "^1.1.1", + "zone.js": "^0.7.2" + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/rollup.config.js b/src/ui_ng/lib/rollup.config.js new file mode 100644 index 000000000..d2b19bfd0 --- /dev/null +++ b/src/ui_ng/lib/rollup.config.js @@ -0,0 +1,18 @@ +export default { + entry: 'dist/index.js', + dest: 'dist/bundles/harborui.umd.js', + sourceMap: false, + format: 'umd', + moduleName: 'harborui', + globals: { + '@angular/core': 'ng.core', + 'rxjs/Observable': 'Rx', + 'rxjs/ReplaySubject': 'Rx', + 'rxjs/add/operator/map': 'Rx.Observable.prototype', + 'rxjs/add/operator/mergeMap': 'Rx.Observable.prototype', + 'rxjs/add/operator/catch': 'Rx.Observable.prototype', + 'rxjs/add/operator/toPromise': 'Rx.Observable.prototype', + 'rxjs/add/observable/of': 'Rx.Observable', + 'rxjs/add/observable/throw': 'Rx.Observable' + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/harbor-library.module.ts b/src/ui_ng/lib/src/harbor-library.module.ts new file mode 100644 index 000000000..adf42683e --- /dev/null +++ b/src/ui_ng/lib/src/harbor-library.module.ts @@ -0,0 +1,45 @@ +import { NgModule, ModuleWithProviders, Provider } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SYSTEMINFO_DIRECTIVES } from './system/index'; +import { SERVICE_CONFIG, IServiceConfig } from './service.config'; + +export const DefaultServiceConfig: IServiceConfig = { + systemInfoEndpoint: "/api/system", + repositoryBaseEndpoint: "", + logBaseEndpoint: "", + targetBaseEndpoint: "", + replicationRuleEndpoint:"", + replicationJobEndpoint: "" +}; + +export interface HarborModuleConfig { + config?: Provider +} + +@NgModule({ + imports: [ + CommonModule + ], + declarations: [SYSTEMINFO_DIRECTIVES], + exports: [SYSTEMINFO_DIRECTIVES] +}) + +export class HarborLibraryModule { + static forRoot(config: HarborModuleConfig = {}): ModuleWithProviders { + return { + ngModule: HarborLibraryModule, + providers: [ + config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig } + ] + }; + } + + static forChild(config: HarborModuleConfig = {}): ModuleWithProviders { + return { + ngModule: HarborLibraryModule, + providers: [ + config.config || { provide: SERVICE_CONFIG, useValue: DefaultServiceConfig } + ] + }; + } +} diff --git a/src/ui_ng/lib/src/index.ts b/src/ui_ng/lib/src/index.ts new file mode 100644 index 000000000..a8b470020 --- /dev/null +++ b/src/ui_ng/lib/src/index.ts @@ -0,0 +1,4 @@ +export * from './harbor-library.module'; +export * from './system/index'; +export * from './service.config'; +export * from './service/index'; \ No newline at end of file diff --git a/src/ui_ng/lib/src/service.config.ts b/src/ui_ng/lib/src/service.config.ts new file mode 100644 index 000000000..36aeaa54c --- /dev/null +++ b/src/ui_ng/lib/src/service.config.ts @@ -0,0 +1,64 @@ +import { OpaqueToken } from '@angular/core'; + +export let SERVICE_CONFIG = new OpaqueToken("service.config"); + +export interface IServiceConfig { + systemInfoEndpoint: string; + + /** + * The base endpoint of the service used to handle the repositories of registry and/or tags of repository. + * The endpoints of repository or tag(s) will be built based on this endpoint. + * E.g: + * If the base endpoint is '/api/repositories', + * the repository endpoint will be '/api/repositories/:repo_id', + * the tag(s) endpoint will be '/api/repositories/:repo_id/tags[/:tag_id]'. + * + * + * @type {string} + * @memberOf IServiceConfig + */ + repositoryBaseEndpoint: string; + + /** + * The base endpoint of the service used to handle the recent access logs. + * + * @type {string} + * @memberOf IServiceConfig + */ + logBaseEndpoint: string; + + /** + * The base endpoint of the service used to handle the registry targets. + * Registry target related endpoints will be built based on this endpoint. + * E.g: + * If the base endpoint is '/api/endpoints', + * the endpoint for registry target will be '/api/endpoints/:endpoint_id', + * the endpoint for pinging registry target will be '/api/endpoints/:endpoint_id/ping'. + * + * @type {string} + * @memberOf IServiceConfig + */ + targetBaseEndpoint: string; + + /** + * The base endpoint of the service used to handle the replication rules. + * Replication rule related endpoints will be built based on this endpoint. + * E.g: + * If the base endpoint is '/api/replication/rules', + * the endpoint for rule will be '/api/replication/rules/:rule_id'. + * + * @type {string} + * @memberOf IServiceConfig + */ + replicationRuleEndpoint: string; + + + /** + * The base endpoint of the service used to handle the replication jobs. + * + * + * @type {string} + * @memberOf IServiceConfig + */ + replicationJobEndpoint: string; +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/RequestQueryParams.ts b/src/ui_ng/lib/src/service/RequestQueryParams.ts new file mode 100644 index 000000000..d7d098a5a --- /dev/null +++ b/src/ui_ng/lib/src/service/RequestQueryParams.ts @@ -0,0 +1,14 @@ +import { URLSearchParams } from '@angular/http'; + +/** + * Wrap the class 'URLSearchParams' for future extending requirements. + * Currently no extra methods provided. + * + * @export + * @class RequestQueryParams + * @extends {URLSearchParams} + */ +export class RequestQueryParams extends URLSearchParams { + + constructor() { super(); } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/access-log.service.spec.ts b/src/ui_ng/lib/src/service/access-log.service.spec.ts new file mode 100644 index 000000000..fa874492d --- /dev/null +++ b/src/ui_ng/lib/src/service/access-log.service.spec.ts @@ -0,0 +1,18 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AccessLogService, AccessLogDefaultService } from './access-log.service'; + +describe('AccessLogService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ + provide: AccessLogService, + useClass: AccessLogDefaultService + }] + }); + }); + + it('should ...', inject([AccessLogDefaultService], (service: AccessLogService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/ui_ng/lib/src/service/access-log.service.ts b/src/ui_ng/lib/src/service/access-log.service.ts new file mode 100644 index 000000000..72ae02931 --- /dev/null +++ b/src/ui_ng/lib/src/service/access-log.service.ts @@ -0,0 +1,58 @@ +import { Observable } from 'rxjs/Observable'; +import { RequestQueryParams } from './RequestQueryParams'; +import { AccessLog } from './interface'; +import { Injectable } from "@angular/core"; +import 'rxjs/add/observable/of'; + +/** + * Define service methods to handle the access log related things. + * + * @export + * @abstract + * @class AccessLogService + */ +export abstract class AccessLogService { + /** + * Get the audit logs for the specified project. + * Set query parameters through 'queryParams', support: + * - page + * - pageSize + * + * @abstract + * @param {(number | string)} projectId + * @param {RequestQueryParams} [queryParams] + * @returns {(Observable | AccessLog[])} + * + * @memberOf AccessLogService + */ + abstract getAuditLogs(projectId: number | string, queryParams?: RequestQueryParams): Observable | AccessLog[]; + + /** + * Get the recent logs. + * + * @abstract + * @param {number} lines : Specify how many lines should be returned. + * @returns {(Observable | AccessLog[])} + * + * @memberOf AccessLogService + */ + abstract getRecentLogs(lines: number): Observable | AccessLog[]; +} + +/** + * Implement a default service for access log. + * + * @export + * @class AccessLogDefaultService + * @extends {AccessLogService} + */ +@Injectable() +export class AccessLogDefaultService extends AccessLogService { + public getAuditLogs(projectId: number | string, queryParams?: RequestQueryParams): Observable | AccessLog[] { + return Observable.of([]); + } + + public getRecentLogs(lines: number): Observable | AccessLog[] { + return Observable.of([]); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/endpoint.service.spec.ts b/src/ui_ng/lib/src/service/endpoint.service.spec.ts new file mode 100644 index 000000000..304ad55cb --- /dev/null +++ b/src/ui_ng/lib/src/service/endpoint.service.spec.ts @@ -0,0 +1,18 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { EndpointService, EndpointDefaultService } from './endpoint.service'; + +describe('EndpointService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ + provide: EndpointService, + useClass: EndpointDefaultService + }] + }); + }); + + it('should ...', inject([EndpointDefaultService], (service: EndpointService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/ui_ng/lib/src/service/endpoint.service.ts b/src/ui_ng/lib/src/service/endpoint.service.ts new file mode 100644 index 000000000..279e0fe7c --- /dev/null +++ b/src/ui_ng/lib/src/service/endpoint.service.ts @@ -0,0 +1,118 @@ +import { Observable } from 'rxjs/Observable'; +import { RequestQueryParams } from './RequestQueryParams'; +import { Endpoint } from './interface'; +import { Injectable } from "@angular/core"; +import 'rxjs/add/observable/of'; + +/** + * Define the service methods to handle the endpoint related things. + * + * @export + * @abstract + * @class EndpointService + */ +export abstract class EndpointService { + /** + * Get all the endpoints. + * Set the argument 'endpointName' to return only the endpoints match the name pattern. + * + * @abstract + * @param {string} [endpointName] + * @param {RequestQueryParams} [queryParams] + * @returns {(Observable | Endpoint[])} + * + * @memberOf EndpointService + */ + abstract getEndpoints(endpointName?: string, queryParams?: RequestQueryParams): Observable | Endpoint[]; + + /** + * Get the specified endpoint. + * + * @abstract + * @param {(number | string)} endpointId + * @returns {(Observable | Endpoint)} + * + * @memberOf EndpointService + */ + abstract getEndpoint(endpointId: number | string): Observable | Endpoint; + + /** + * Create new endpoint. + * + * @abstract + * @param {Endpoint} endpoint + * @returns {(Observable | any)} + * + * @memberOf EndpointService + */ + abstract createEndpoint(endpoint: Endpoint): Observable | any; + + /** + * Update the specified endpoint. + * + * @abstract + * @param {(number | string)} endpointId + * @param {Endpoint} endpoint + * @returns {(Observable | any)} + * + * @memberOf EndpointService + */ + abstract updateEndpoint(endpointId: number | string, endpoint: Endpoint): Observable | any; + + /** + * Delete the specified endpoint. + * + * @abstract + * @param {(number | string)} endpointId + * @returns {(Observable | any)} + * + * @memberOf EndpointService + */ + abstract deleteEndpoint(endpointId: number | string): Observable | any; + + /** + * Ping the specified endpoint. + * + * @abstract + * @param {Endpoint} endpoint + * @returns {(Observable | any)} + * + * @memberOf EndpointService + */ + abstract pingEndpoint(endpoint: Endpoint): Observable | any; +} + +/** + * Implement default service for endpoint. + * + * @export + * @class EndpointDefaultService + * @extends {EndpointService} + */ +@Injectable() +export class EndpointDefaultService extends EndpointService { + public getEndpoints(endpointName?: string, queryParams?: RequestQueryParams): Observable | Endpoint[] { + return Observable.of([]); + } + + public getEndpoint(endpointId: number | string): Observable | Endpoint { + return Observable.of({}); + } + + public createEndpoint(endpoint: Endpoint): Observable | any { + return Observable.of({}); + } + + public updateEndpoint(endpointId: number | string, endpoint: Endpoint): Observable | any { + return Observable.of({}); + } + + public deleteEndpoint(endpointId: number | string): Observable | any { + return Observable.of({}); + } + + public pingEndpoint(endpoint: Endpoint): Observable | any { + return Observable.of({}); + } +} + diff --git a/src/ui_ng/lib/src/service/index.ts b/src/ui_ng/lib/src/service/index.ts new file mode 100644 index 000000000..e57632c6a --- /dev/null +++ b/src/ui_ng/lib/src/service/index.ts @@ -0,0 +1,7 @@ +export * from './interface'; +export * from './access-log.service'; +export * from './endpoint.service'; +export * from './replication.service'; +export * from './repository.service'; +export * from './tag.service'; +export * from './RequestQueryParams'; \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/interface.ts b/src/ui_ng/lib/src/service/interface.ts new file mode 100644 index 000000000..e6677597a --- /dev/null +++ b/src/ui_ng/lib/src/service/interface.ts @@ -0,0 +1,63 @@ +/** + * The base interface contains the general properties + * + * @export + * @interface Base + */ +export interface Base { + id?: string; + name?: string; + creation_time?: Date; + update_time?: Date; +} + +/** + * Interface for Repository + * + * @export + * @interface Repository + * @extends {Base} + */ +export interface Repository extends Base { } + +/** + * Interface for the tag of repository + * + * @export + * @interface Tag + * @extends {Base} + */ +export interface Tag extends Base { } + +/** + * Interface for registry endpoints. + * + * @export + * @interface Endpoint + * @extends {Base} + */ +export interface Endpoint extends Base { } + +/** + * Interface for replication rule. + * + * @export + * @interface ReplicationRule + */ +export interface ReplicationRule { } + +/** + * Interface for replication job. + * + * @export + * @interface ReplicationJob + */ +export interface ReplicationJob { } + +/** + * Interface for access log. + * + * @export + * @interface AccessLog + */ +export interface AccessLog { } \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/replication.service.spec.ts b/src/ui_ng/lib/src/service/replication.service.spec.ts new file mode 100644 index 000000000..3c6786fb1 --- /dev/null +++ b/src/ui_ng/lib/src/service/replication.service.spec.ts @@ -0,0 +1,18 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { ReplicationService, ReplicationDefaultService } from './replication.service'; + +describe('ReplicationService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ + provide: ReplicationService, + useClass: ReplicationDefaultService + }] + }); + }); + + it('should ...', inject([ReplicationDefaultService], (service: ReplicationService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/ui_ng/lib/src/service/replication.service.ts b/src/ui_ng/lib/src/service/replication.service.ts new file mode 100644 index 000000000..e70780fd6 --- /dev/null +++ b/src/ui_ng/lib/src/service/replication.service.ts @@ -0,0 +1,157 @@ +import { Observable } from 'rxjs/Observable'; +import { RequestQueryParams } from './RequestQueryParams'; +import { ReplicationJob, ReplicationRule } from './interface'; +import { Injectable } from "@angular/core"; +import 'rxjs/add/observable/of'; + +/** + * Define the service methods to handle the replication (rule and job) related things. + * + * @export + * @abstract + * @class ReplicationService + */ +export abstract class ReplicationService { + /** + * Get the replication rules. + * Set the argument 'projectId' to limit the data scope to the specified project; + * set the argument 'ruleName' to return the rule only match the name pattern; + * if pagination needed, use the queryParams to add query parameters. + * + * @abstract + * @param {(number | string)} [projectId] + * @param {string} [ruleName] + * @param {RequestQueryParams} [queryParams] + * @returns {(Observable | ReplicationRule[])} + * + * @memberOf ReplicationService + */ + abstract getReplicationRules(projectId?: number | string, ruleName?: string, queryParams?: RequestQueryParams): Observable | ReplicationRule[]; + + /** + * Get the specified replication rule. + * + * @abstract + * @param {(number | string)} ruleId + * @returns {(Observable | ReplicationRule)} + * + * @memberOf ReplicationService + */ + abstract getReplicationRule(ruleId: number | string): Observable | ReplicationRule; + + /** + * Create new replication rule. + * + * @abstract + * @param {ReplicationRule} replicationRule + * @returns {(Observable | any)} + * + * @memberOf ReplicationService + */ + abstract createReplicationRule(replicationRule: ReplicationRule): Observable | any; + + /** + * Update the specified replication rule. + * + * @abstract + * @param {ReplicationRule} replicationRule + * @returns {(Observable | any)} + * + * @memberOf ReplicationService + */ + abstract updateReplicationRule(replicationRule: ReplicationRule): Observable | any; + + /** + * Delete the specified replication rule. + * + * @abstract + * @param {(number | string)} ruleId + * @returns {(Observable | any)} + * + * @memberOf ReplicationService + */ + abstract deleteReplicationRule(ruleId: number | string): Observable | any; + + /** + * Enable the specified replication rule. + * + * @abstract + * @param {(number | string)} ruleId + * @returns {(Observable | any)} + * + * @memberOf ReplicationService + */ + abstract enableReplicationRule(ruleId: number | string): Observable | any; + + /** + * Disable the specified replication rule. + * + * @abstract + * @param {(number | string)} ruleId + * @returns {(Observable | any)} + * + * @memberOf ReplicationService + */ + abstract disableReplicationRule(ruleId: number | string): Observable | any; + + /** + * Get the jobs for the specified replication rule. + * Set query parameters through 'queryParams', support: + * - status + * - repository + * - startTime and endTime + * - page + * - pageSize + * + * @abstract + * @param {(number | string)} ruleId + * @param {RequestQueryParams} [queryParams] + * @returns {(Observable | ReplicationJob)} + * + * @memberOf ReplicationService + */ + abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable | ReplicationJob[]; + +} + +/** + * Implement default service for replication rule and job. + * + * @export + * @class ReplicationDefaultService + * @extends {ReplicationService} + */ +@Injectable() +export class ReplicationDefaultService extends ReplicationService { + public getReplicationRules(projectId?: number | string, ruleName?: string, queryParams?: RequestQueryParams): Observable | ReplicationRule[] { + return Observable.of([]); + } + + public getReplicationRule(ruleId: number | string): Observable | ReplicationRule { + return Observable.of({}); + } + + public createReplicationRule(replicationRule: ReplicationRule): Observable | any { + return Observable.of({}); + } + + public updateReplicationRule(replicationRule: ReplicationRule): Observable | any { + return Observable.of({}); + } + + public deleteReplicationRule(ruleId: number | string): Observable | any { + return Observable.of({}); + } + + public enableReplicationRule(ruleId: number | string): Observable | any { + return Observable.of({}); + } + + public disableReplicationRule(ruleId: number | string): Observable | any { + return Observable.of({}); + } + + public getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable | ReplicationJob[] { + return Observable.of([]); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/repository.service.spec.ts b/src/ui_ng/lib/src/service/repository.service.spec.ts new file mode 100644 index 000000000..3c6786fb1 --- /dev/null +++ b/src/ui_ng/lib/src/service/repository.service.spec.ts @@ -0,0 +1,18 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { ReplicationService, ReplicationDefaultService } from './replication.service'; + +describe('ReplicationService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ + provide: ReplicationService, + useClass: ReplicationDefaultService + }] + }); + }); + + it('should ...', inject([ReplicationDefaultService], (service: ReplicationService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/ui_ng/lib/src/service/repository.service.ts b/src/ui_ng/lib/src/service/repository.service.ts new file mode 100644 index 000000000..68f05c485 --- /dev/null +++ b/src/ui_ng/lib/src/service/repository.service.ts @@ -0,0 +1,61 @@ +import { Observable } from 'rxjs/Observable'; +import { RequestQueryParams } from './RequestQueryParams'; +import { Repository } from './interface'; +import { Injectable } from "@angular/core"; +import 'rxjs/add/observable/of'; + +/** + * Define service methods for handling the repository related things. + * Loose couple with project module. + * + * @export + * @abstract + * @class RepositoryService + */ +export abstract class RepositoryService { + /** + * List all the repositories in the specified project. + * Specify the 'repositoryName' to only return the repositories which match the name pattern. + * If pagination needed, set the following parameters in queryParams: + * 'page': current page, + * 'page_size': page size. + * + * @abstract + * @param {(number | string)} projectId + * @param {string} repositoryName + * @param {RequestQueryParams} [queryParams] + * @returns {(Observable | Repository[])} + * + * @memberOf RepositoryService + */ + abstract getRepositories(projectId: number | string, repositoryName?: string, queryParams?: RequestQueryParams): Observable | Repository[]; + + /** + * DELETE the specified repository. + * + * @abstract + * @param {string} repositoryName + * @returns {(Observable | any)} + * + * @memberOf RepositoryService + */ + abstract deleteRepository(repositoryName: string): Observable | any; +} + +/** + * Implement default service for repository. + * + * @export + * @class RepositoryDefaultService + * @extends {RepositoryService} + */ +@Injectable() +export class RepositoryDefaultService extends RepositoryService { + public getRepositories(projectId: number | string, repositoryName?: string, queryParams?: RequestQueryParams): Observable | Repository[] { + return Observable.of([]); + } + + public deleteRepository(repositoryName: string): Observable | any { + return Observable.of({}); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/service/tag.service.spec.ts b/src/ui_ng/lib/src/service/tag.service.spec.ts new file mode 100644 index 000000000..cd375a8eb --- /dev/null +++ b/src/ui_ng/lib/src/service/tag.service.spec.ts @@ -0,0 +1,18 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { TagService, TagDefaultService } from './tag.service'; + +describe('TagService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ + provide: TagService, + useClass: TagDefaultService + }] + }); + }); + + it('should ...', inject([TagDefaultService], (service: TagService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/ui_ng/lib/src/service/tag.service.ts b/src/ui_ng/lib/src/service/tag.service.ts new file mode 100644 index 000000000..3d66465c2 --- /dev/null +++ b/src/ui_ng/lib/src/service/tag.service.ts @@ -0,0 +1,57 @@ +import { Observable } from 'rxjs/Observable'; +import { RequestQueryParams } from './RequestQueryParams'; +import { Tag } from './interface'; +import { Injectable } from "@angular/core"; +import 'rxjs/add/observable/of'; + +/** + * Define the service methods to handle the repository tag related things. + * + * @export + * @abstract + * @class TagService + */ +export abstract class TagService { + /** + * Get all the tags under the specified repository. + * NOTES: If the Notary is enabled, the signatures should be included in the returned data. + * + * @abstract + * @param {string} repositoryName + * @param {RequestQueryParams} [queryParams] + * @returns {(Observable | Tag[])} + * + * @memberOf TagService + */ + abstract getTags(repositoryName: string, queryParams?: RequestQueryParams): Observable | Tag[]; + + /** + * Delete the specified tag. + * + * @abstract + * @param {string} repositoryName + * @param {string} tag + * @returns {(Observable | any)} + * + * @memberOf TagService + */ + abstract deleteTag(repositoryName: string, tag: string): Observable | any; +} + +/** + * Implement default service for tag. + * + * @export + * @class TagDefaultService + * @extends {TagService} + */ +@Injectable() +export class TagDefaultService extends TagService { + public getTags(repositoryName: string, queryParams?: RequestQueryParams): Observable | Tag[] { + return Observable.of([]); + } + + public deleteTag(repositoryName: string, tag: string): Observable | any { + return Observable.of({}); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/system/index.ts b/src/ui_ng/lib/src/system/index.ts new file mode 100644 index 000000000..02c60e78e --- /dev/null +++ b/src/ui_ng/lib/src/system/index.ts @@ -0,0 +1,8 @@ +import {Type} from "@angular/core"; +import { SystemComponent } from './system.component'; + +export * from "./system.component"; + +export const SYSTEMINFO_DIRECTIVES: Type[] = [ + SystemComponent +]; \ No newline at end of file diff --git a/src/ui_ng/lib/src/system/providers/system-info.service.spec.ts b/src/ui_ng/lib/src/system/providers/system-info.service.spec.ts new file mode 100644 index 000000000..cd1f7000d --- /dev/null +++ b/src/ui_ng/lib/src/system/providers/system-info.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { SystemInfoService } from './system-info.service'; + +describe('SysteninfoService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [SystemInfoService] + }); + }); + + it('should ...', inject([SystemInfoService], (service: SystemInfoService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/ui_ng/lib/src/system/providers/system-info.service.ts b/src/ui_ng/lib/src/system/providers/system-info.service.ts new file mode 100644 index 000000000..e090d4829 --- /dev/null +++ b/src/ui_ng/lib/src/system/providers/system-info.service.ts @@ -0,0 +1,29 @@ +import { Injectable, Inject } from '@angular/core'; +import { Http, RequestOptions, Headers } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { SERVICE_CONFIG, IServiceConfig } from '../../service.config'; + +@Injectable() +export class SystemInfoService { + httpOptions = new RequestOptions({ + headers: new Headers({ + "Content-Type": 'application/json' + }) + }); + + constructor( + private http: Http, + @Inject(SERVICE_CONFIG) private config: IServiceConfig) { } + + getSystemInfo(): Promise { + if(this.config.systemInfoEndpoint.trim() === "") { + return Promise.reject("500: Internal error"); + } + + return this.http.get(this.config.systemInfoEndpoint, this.httpOptions).toPromise() + .then(response => response.json()) + .catch(error => console.error("Get systeminfo error: ", error)); + } + +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/system/system.component.spec.ts b/src/ui_ng/lib/src/system/system.component.spec.ts new file mode 100644 index 000000000..05d48af03 --- /dev/null +++ b/src/ui_ng/lib/src/system/system.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SystemComponent } from './system.component'; + +describe('SystemComponent', () => { + let component: SystemComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [SystemComponent] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SystemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/ui_ng/lib/src/system/system.component.ts b/src/ui_ng/lib/src/system/system.component.ts new file mode 100644 index 000000000..4afa2750c --- /dev/null +++ b/src/ui_ng/lib/src/system/system.component.ts @@ -0,0 +1,34 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { SystemInfoService } from './providers/system-info.service'; + +@Component({ + selector: 'hbr-system', + template: ` +
+      {{info}}
+    
+ `, + styles: [], + providers: [SystemInfoService] +}) + +export class SystemComponent implements OnInit { + _systemInfo: string = "Loading..."; + + constructor(private systemService: SystemInfoService) { } + + public get info(): string { + return this._systemInfo; + } + + ngOnInit() { + this.getInfo(); + } + + getInfo(): void { + this.systemService.getSystemInfo() + .then((res: any) => this._systemInfo = JSON.stringify(res)) + .catch(error => console.error("Retrieve system info error: ", error)); + } + +} diff --git a/src/ui_ng/lib/tsconfig.json b/src/ui_ng/lib/tsconfig.json new file mode 100644 index 000000000..90bd8fce5 --- /dev/null +++ b/src/ui_ng/lib/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "declaration": true, + "stripInternal": true, + "experimentalDecorators": true, + "strictNullChecks": true, + "noImplicitAny": true, + "module": "es2015", + "moduleResolution": "node", + "paths": { + "@angular/core": ["node_modules/@angular/core"], + "rxjs/*": ["node_modules/rxjs/*"] + }, + "rootDir": ".", + "outDir": "dist", + "sourceMap": true, + "inlineSources": true, + "target": "es5", + "skipLibCheck": true, + "lib": [ + "es2015", + "dom" + ] + }, + "files": [ + "index.ts" + ], + "angularCompilerOptions": { + "strictMetadataEmit": true + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/tslint.json b/src/ui_ng/lib/tslint.json new file mode 100644 index 000000000..ad0093e94 --- /dev/null +++ b/src/ui_ng/lib/tslint.json @@ -0,0 +1,114 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "indent": [ + true, + "spaces" + ], + "label-position": true, + "label-undefined": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-key": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-eval": true, + "no-inferrable-types": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-unused-variable": true, + "no-unreachable": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "directive-selector-prefix": [true, "app"], + "component-selector-prefix": [true, "app"], + "directive-selector-name": [true, "camelCase"], + "component-selector-name": [true, "kebab-case"], + "directive-selector-type": [true, "attribute"], + "component-selector-type": [true, "element"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true, + "templates-use-public": true, + "invoke-injectable": true + } +} diff --git a/src/ui_ng/lib/typings.json b/src/ui_ng/lib/typings.json new file mode 100644 index 000000000..bde32e5e0 --- /dev/null +++ b/src/ui_ng/lib/typings.json @@ -0,0 +1,5 @@ +{ + "globalDependencies": { + "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504" + } +} \ No newline at end of file diff --git a/src/ui_ng/logo.png b/src/ui_ng/logo.png new file mode 100644 index 000000000..274e12ef9 Binary files /dev/null and b/src/ui_ng/logo.png differ diff --git a/src/ui_ng/package.json b/src/ui_ng/package.json new file mode 100644 index 000000000..0e0eacf60 --- /dev/null +++ b/src/ui_ng/package.json @@ -0,0 +1,63 @@ +{ + "name": "harbor", + "version": "0.6.0", + "description": "Harbor UI with Clarity", + "angular-cli": {}, + "scripts": { + "start": "ng serve --ssl 1 --ssl-key ssl/server.key --ssl-cert ssl/server.crt --host 0.0.0.0 --proxy-config proxy.config.json", + "lint": "tslint \"src/**/*.ts\"", + "test": "ng test --single-run", + "pree2e": "webdriver-manager update", + "e2e": "protractor" + }, + "private": true, + "dependencies": { + "@angular/common": "^2.4.1", + "@angular/compiler": "^2.4.1", + "@angular/core": "^2.4.1", + "@angular/forms": "^2.4.1", + "@angular/http": "^2.4.1", + "@angular/platform-browser": "^2.4.1", + "@angular/platform-browser-dynamic": "^2.4.1", + "@angular/router": "^3.4.1", + "@ngx-translate/core": "^6.0.0", + "@ngx-translate/http-loader": "0.0.3", + "@webcomponents/custom-elements": "1.0.0-alpha.3", + "angular2-cookie": "^1.2.6", + "clarity-angular": "0.8.7", + "clarity-icons": "0.8.7", + "clarity-ui": "0.8.7", + "core-js": "^2.4.1", + "fs": "0.0.1-security", + "jquery": "^2.2.4", + "mutationobserver-shim": "^0.3.2", + "rxjs": "^5.0.1", + "ts-helpers": "^1.1.1", + "web-animations-js": "^2.2.1", + "zone.js": "^0.7.2" + }, + "devDependencies": { + "@angular/compiler-cli": "^2.4.1", + "@angular/cli": "^1.0.0", + "@types/core-js": "^0.9.34", + "@types/jasmine": "^2.2.30", + "@types/node": "^6.0.42", + "bootstrap": "4.0.0-alpha.5", + "codelyzer": "~1.0.0-beta.3", + "enhanced-resolve": "^3.0.0", + "jasmine-core": "2.4.1", + "jasmine-spec-reporter": "2.5.0", + "karma": "1.2.0", + "karma-cli": "^1.0.1", + "karma-jasmine": "^1.0.2", + "karma-mocha-reporter": "^2.2.1", + "karma-phantomjs-launcher": "^1.0.0", + "karma-remap-istanbul": "^0.2.1", + "protractor": "4.0.9", + "ts-node": "1.2.1", + "tslint": "^4.1.1", + "typescript": "~2.2.1", + "typings": "^1.4.0", + "webdriver-manager": "10.2.5" + } +} \ No newline at end of file diff --git a/src/ui_ng/protractor.config.js b/src/ui_ng/protractor.config.js new file mode 100644 index 000000000..169743b34 --- /dev/null +++ b/src/ui_ng/protractor.config.js @@ -0,0 +1,32 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/docs/referenceConf.js + +/*global jasmine */ +var SpecReporter = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './e2e/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + useAllAngular2AppRoots: true, + beforeLaunch: function() { + require('ts-node').register({ + project: 'e2e' + }); + }, + onPrepare: function() { + jasmine.getEnv().addReporter(new SpecReporter()); + } +}; diff --git a/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.html b/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.html new file mode 100644 index 000000000..df0fe84f9 --- /dev/null +++ b/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.html @@ -0,0 +1,51 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.ts b/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.ts new file mode 100644 index 000000000..4ad0d15d6 --- /dev/null +++ b/src/ui_ng/src/app/account/account-settings/account-settings-modal.component.ts @@ -0,0 +1,223 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild, AfterViewChecked } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { SessionUser } from '../../shared/session-user'; +import { SessionService } from '../../shared/session.service'; +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +@Component({ + selector: "account-settings-modal", + templateUrl: "account-settings-modal.component.html", + styleUrls: ['../../common.css'] +}) + +export class AccountSettingsModalComponent implements OnInit, AfterViewChecked { + opened: boolean = false; + staticBackdrop: boolean = true; + account: SessionUser; + error: any = null; + originalStaticData: SessionUser; + private emailTooltip: string = 'TOOLTIP.EMAIL'; + private validationStateMap: any = { + "account_settings_email": true, + "account_settings_full_name": true + }; + private mailAlreadyChecked = {}; + + private isOnCalling: boolean = false; + private formValueChanged: boolean = false; + private checkOnGoing: boolean = false; + + accountFormRef: NgForm; + @ViewChild("accountSettingsFrom") accountForm: NgForm; + @ViewChild(InlineAlertComponent) + private inlineAlert: InlineAlertComponent; + + constructor( + private session: SessionService, + private msgHandler: MessageHandlerService) { } + + ngOnInit(): void { + //Value copy + this.account = Object.assign({}, this.session.getCurrentUser()); + } + + private getValidationState(key: string): boolean { + return this.validationStateMap[key]; + } + + private handleValidation(key: string, flag: boolean): void { + if (flag) { + //Checking + let cont = this.accountForm.controls[key]; + if (cont) { + this.validationStateMap[key] = cont.valid; + //Check email existing from backend + if (cont.valid && key === "account_settings_email") { + if (this.formValueChanged && this.account.email != this.originalStaticData.email) { + if (this.mailAlreadyChecked[this.account.email]) { + this.validationStateMap[key] = !this.mailAlreadyChecked[this.account.email].result; + if (!this.validationStateMap[key]) { + this.emailTooltip = "TOOLTIP.EMAIL_EXISTING"; + } + return; + } + + //Mail changed + this.checkOnGoing = true; + this.session.checkUserExisting("email", this.account.email) + .then((res: boolean) => { + this.checkOnGoing = false; + this.validationStateMap[key] = !res; + if (res) { + this.emailTooltip = "TOOLTIP.EMAIL_EXISTING"; + } + this.mailAlreadyChecked[this.account.email] = { + result: res + }; //Tag it checked + }) + .catch(error => { + this.checkOnGoing = false; + this.validationStateMap[key] = false;//Not valid @ backend + }); + } + } + } + } else { + //Reset + this.validationStateMap[key] = true; + this.emailTooltip = "TOOLTIP.EMAIL"; + } + } + + private isUserDataChange(): boolean { + if (!this.originalStaticData || !this.account) { + return false; + } + + for (var prop in this.originalStaticData) { + if (this.originalStaticData[prop] != this.account[prop]) { + return true; + } + } + + return false; + } + + public get isValid(): boolean { + return this.accountForm && + this.accountForm.valid && + this.error === null && + this.validationStateMap["account_settings_email"]; //backend check is valid as well + } + + public get showProgress(): boolean { + return this.isOnCalling; + } + + public get checkProgress(): boolean { + return this.checkOnGoing; + } + + ngAfterViewChecked(): void { + if (this.accountFormRef != this.accountForm) { + this.accountFormRef = this.accountForm; + if (this.accountFormRef) { + this.accountFormRef.valueChanges.subscribe(data => { + if (this.error) { + this.error = null; + } + this.formValueChanged = true; + this.inlineAlert.close(); + }); + } + } + } + + open() { + //Keep the initial data for future diff + this.originalStaticData = Object.assign({}, this.session.getCurrentUser()); + this.account = Object.assign({}, this.session.getCurrentUser()); + this.formValueChanged = false; + + //Confirm inline alert is closed + this.inlineAlert.close(); + + //Clear check history + this.mailAlreadyChecked = {}; + + //Reset validation status + this.validationStateMap = { + "account_settings_email": true, + "account_settings_full_name": true + }; + + this.opened = true; + } + + close() { + if (this.formValueChanged) { + if (!this.isUserDataChange()) { + this.opened = false; + } else { + //Need user confirmation + this.inlineAlert.showInlineConfirmation({ + message: "ALERT.FORM_CHANGE_CONFIRMATION" + }); + } + } else { + this.opened = false; + } + } + + submit() { + if (!this.isValid || this.isOnCalling) { + return; + } + + //Double confirm session is valid + let cUser = this.session.getCurrentUser(); + if (!cUser) { + return; + } + + this.isOnCalling = true; + + this.session.updateAccountSettings(this.account) + .then(() => { + this.isOnCalling = false; + this.opened = false; + this.msgHandler.showSuccess("PROFILE.SAVE_SUCCESS"); + }) + .catch(error => { + this.isOnCalling = false; + this.error = error; + if (this.msgHandler.isAppLevel(error)) { + this.opened = false; + this.msgHandler.handleError(error); + } else { + this.inlineAlert.showInlineError(error); + } + }); + } + + confirmCancel(): void { + this.inlineAlert.close(); + this.opened = false; + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/account.module.ts b/src/ui_ng/src/app/account/account.module.ts new file mode 100644 index 000000000..efbe02663 --- /dev/null +++ b/src/ui_ng/src/app/account/account.module.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CoreModule } from '../core/core.module'; + +import { SignInComponent } from './sign-in/sign-in.component'; +import { PasswordSettingComponent } from './password/password-setting.component'; +import { AccountSettingsModalComponent } from './account-settings/account-settings-modal.component'; +import { SharedModule } from '../shared/shared.module'; +import { SignUpComponent } from './sign-up/sign-up.component'; +import { ForgotPasswordComponent } from './password/forgot-password.component'; +import { ResetPasswordComponent } from './password/reset-password.component'; +import { SignUpPageComponent } from './sign-up/sign-up-page.component'; + +import { PasswordSettingService } from './password/password-setting.service'; +import { RepositoryModule } from '../repository/repository.module'; + +@NgModule({ + imports: [ + CoreModule, + RouterModule, + SharedModule, + RepositoryModule + ], + declarations: [ + SignInComponent, + PasswordSettingComponent, + AccountSettingsModalComponent, + SignUpComponent, + ForgotPasswordComponent, + ResetPasswordComponent, + SignUpPageComponent], + exports: [ + SignInComponent, + PasswordSettingComponent, + AccountSettingsModalComponent, + ResetPasswordComponent, + SignUpPageComponent], + + providers: [PasswordSettingService] +}) +export class AccountModule { } \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/forgot-password.component.html b/src/ui_ng/src/app/account/password/forgot-password.component.html new file mode 100644 index 000000000..5f50299e2 --- /dev/null +++ b/src/ui_ng/src/app/account/password/forgot-password.component.html @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/forgot-password.component.ts b/src/ui_ng/src/app/account/password/forgot-password.component.ts new file mode 100644 index 000000000..103d81647 --- /dev/null +++ b/src/ui_ng/src/app/account/password/forgot-password.component.ts @@ -0,0 +1,104 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgForm } from '@angular/forms'; + +import { PasswordSettingService } from './password-setting.service'; +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; + +@Component({ + selector: 'forgot-password', + templateUrl: "forgot-password.component.html", + styleUrls: ['password.component.css', '../../common.css'] +}) +export class ForgotPasswordComponent { + opened: boolean = false; + private onGoing: boolean = false; + private email: string = ""; + private validationState: boolean = true; + private isSuccess: boolean = false; + + @ViewChild("forgotPasswordFrom") forgotPwdForm: NgForm; + @ViewChild(InlineAlertComponent) + private inlineAlert: InlineAlertComponent; + + constructor(private pwdService: PasswordSettingService) { } + + public get showProgress(): boolean { + return this.onGoing; + } + + public get isValid(): boolean { + return this.forgotPwdForm && this.forgotPwdForm.valid ; + } + + public get btnCancelCaption(): string { + if(this.isSuccess){ + return "BUTTON.CLOSE"; + } + + return "BUTTON.CANCEL"; + } + + public open(): void { + //Clear state data + this.validationState = true; + this.isSuccess = false; + this.onGoing = false; + this.email = ""; + this.forgotPwdForm.resetForm(); + this.inlineAlert.close(); + + this.opened = true; + } + + public close(): void { + this.opened = false; + } + + public send(): void { + //Double confirm to avoid improper situations + if (!this.email) { + return; + } + + if (!this.isValid) { + return; + } + + this.onGoing = true; + this.pwdService.sendResetPasswordMail(this.email) + .then(response => { + this.onGoing = false; + this.isSuccess = true; + this.inlineAlert.showInlineSuccess({ + message: "RESET_PWD.SUCCESS" + }); + }) + .catch(error => { + this.onGoing = false; + this.inlineAlert.showInlineError(error); + }) + + } + + public handleValidation(flag: boolean): void { + if (flag) { + this.validationState = true; + } else { + this.validationState = this.isValid; + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/password-setting.component.html b/src/ui_ng/src/app/account/password/password-setting.component.html new file mode 100644 index 000000000..55e396ba5 --- /dev/null +++ b/src/ui_ng/src/app/account/password/password-setting.component.html @@ -0,0 +1,61 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/password-setting.component.ts b/src/ui_ng/src/app/account/password/password-setting.component.ts new file mode 100644 index 000000000..8ce325afc --- /dev/null +++ b/src/ui_ng/src/app/account/password/password-setting.component.ts @@ -0,0 +1,192 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, ViewChild, AfterViewChecked } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgForm } from '@angular/forms'; + +import { PasswordSettingService } from './password-setting.service'; +import { SessionService } from '../../shared/session.service'; +import { isEmptyForm } from '../../shared/shared.utils'; +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +@Component({ + selector: 'password-setting', + templateUrl: "password-setting.component.html", + styleUrls: ['../../common.css'] +}) +export class PasswordSettingComponent implements AfterViewChecked { + opened: boolean = false; + oldPwd: string = ""; + newPwd: string = ""; + reNewPwd: string = ""; + error: any = null; + + private formValueChanged: boolean = false; + private onCalling: boolean = false; + private validationStateMap: any = { + "newPassword": true, + "reNewPassword": true + }; + + pwdFormRef: NgForm; + @ViewChild("changepwdForm") pwdForm: NgForm; + @ViewChild(InlineAlertComponent) + private inlineAlert: InlineAlertComponent; + + constructor( + private passwordService: PasswordSettingService, + private session: SessionService, + private msgHandler: MessageHandlerService) { } + + //If form is valid + public get isValid(): boolean { + if (this.pwdForm && this.pwdForm.form.get("newPassword")) { + return this.pwdForm.valid && + (this.pwdForm.form.get("newPassword").value === this.pwdForm.form.get("reNewPassword").value) && + this.error === null; + } + return false; + } + + public get valueChanged(): boolean { + return this.formValueChanged; + } + + public get showProgress(): boolean { + return this.onCalling; + } + + private getValidationState(key: string): boolean { + return this.validationStateMap[key]; + } + + private handleValidation(key: string, flag: boolean): void { + if (flag) { + //Checking + let cont = this.pwdForm.controls[key]; + if (cont) { + this.validationStateMap[key] = cont.valid; + if (cont.valid) { + if (key === "reNewPassword" || key === "newPassword") { + let cpKey = key === "reNewPassword" ? "newPassword" : "reNewPassword"; + let compareCont = this.pwdForm.controls[cpKey]; + if (compareCont && compareCont.valid) { + this.validationStateMap["reNewPassword"] = cont.value === compareCont.value; + } + } + } + } + } else { + //Reset + this.validationStateMap[key] = true; + } + } + + ngAfterViewChecked() { + if (this.pwdFormRef != this.pwdForm) { + this.pwdFormRef = this.pwdForm; + if (this.pwdFormRef) { + this.pwdFormRef.valueChanges.subscribe(data => { + this.formValueChanged = true; + this.error = null; + this.inlineAlert.close(); + }); + } + } + } + + //Open modal dialog + open(): void { + //Reset state + this.formValueChanged = false; + this.onCalling = false; + this.error = null; + this.validationStateMap = { + "newPassword": true, + "reNewPassword": true + }; + this.pwdForm.reset(); + this.inlineAlert.close(); + + this.opened = true; + } + + //Close the moal dialog + close(): void { + if (this.formValueChanged) { + if (isEmptyForm(this.pwdForm)) { + this.opened = false; + } else { + //Need user confirmation + this.inlineAlert.showInlineConfirmation({ + message: "ALERT.FORM_CHANGE_CONFIRMATION" + }); + } + } else { + this.opened = false; + } + } + + confirmCancel(): void { + this.opened = false; + } + + //handle the ok action + doOk(): void { + if (this.onCalling) { + return;//To avoid duplicate click events + } + + if (!this.isValid) { + return;//Double confirm + } + + //Double confirm session is valid + let cUser = this.session.getCurrentUser(); + if (!cUser) { + return; + } + + //Call service + this.onCalling = true; + + this.passwordService.changePassword(cUser.user_id, + { + new_password: this.pwdForm.value.newPassword, + old_password: this.pwdForm.value.oldPassword + }) + .then(() => { + this.onCalling = false; + this.opened = false + this.msgHandler.showSuccess("CHANGE_PWD.SAVE_SUCCESS"); + }) + .catch(error => { + this.onCalling = false; + this.error = error; + if (this.msgHandler.isAppLevel(error)) { + this.opened = false; + this.msgHandler.handleError(error); + } else { + //Special case for 400 + let msg = '' + error._body; + if (msg && msg.includes('old_password_is_not_correct')) { + this.inlineAlert.showInlineError("INCONRRECT_OLD_PWD"); + } else { + this.inlineAlert.showInlineError(error); + } + } + }); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/password-setting.service.ts b/src/ui_ng/src/app/account/password/password-setting.service.ts new file mode 100644 index 000000000..36be3dde0 --- /dev/null +++ b/src/ui_ng/src/app/account/password/password-setting.service.ts @@ -0,0 +1,87 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions, URLSearchParams } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { PasswordSetting } from './password-setting'; + +const passwordChangeEndpoint = "/api/users/:user_id/password"; +const sendEmailEndpoint = "/sendEmail"; +const resetPasswordEndpoint = "/reset"; + +@Injectable() +export class PasswordSettingService { + private headers: Headers = new Headers({ + "Accept": 'application/json', + "Content-Type": 'application/json' + }); + private options: RequestOptions = new RequestOptions({ + 'headers': this.headers + }); + + constructor(private http: Http) { } + + changePassword(userId: number, setting: PasswordSetting): Promise { + if (!setting || setting.new_password.trim() === "" || setting.old_password.trim() === "") { + return Promise.reject("Invalid data"); + } + + let putUrl = passwordChangeEndpoint.replace(":user_id", userId + ""); + return this.http.put(putUrl, JSON.stringify(setting), this.options) + .toPromise() + .then(() => null) + .catch(error => { + return Promise.reject(error); + }); + } + + sendResetPasswordMail(email: string): Promise { + if (!email) { + return Promise.reject("Invalid email"); + } + + let getUrl = sendEmailEndpoint + "?email=" + email; + return this.http.get(getUrl, this.options).toPromise() + .then(response => response) + .catch(error => { + return Promise.reject(error); + }) + } + + resetPassword(uuid: string, newPassword: string): Promise { + if (!uuid || !newPassword) { + return Promise.reject("Invalid reset uuid or password"); + } + + let formHeaders = new Headers({ + "Content-Type": 'application/x-www-form-urlencoded' + }); + let formOptions: RequestOptions = new RequestOptions({ + headers: formHeaders + }); + + let body: URLSearchParams = new URLSearchParams(); + body.set("reset_uuid", uuid); + body.set("password", newPassword); + + return this.http.post(resetPasswordEndpoint, body.toString(), formOptions) + .toPromise() + .then(response => response) + .catch(error => { + return Promise.reject(error); + }); + } + +} diff --git a/src/ui_ng/src/app/account/password/password-setting.ts b/src/ui_ng/src/app/account/password/password-setting.ts new file mode 100644 index 000000000..3aba8972a --- /dev/null +++ b/src/ui_ng/src/app/account/password/password-setting.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** + * + * Struct for password change + * + * @export + * @class PasswordSetting + */ +export class PasswordSetting { + old_password: string; + new_password: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/password.component.css b/src/ui_ng/src/app/account/password/password.component.css new file mode 100644 index 000000000..da42992cf --- /dev/null +++ b/src/ui_ng/src/app/account/password/password.component.css @@ -0,0 +1,7 @@ +.reset-modal-title-override { + font-size: 14px !important; +} + +.form-group-override { + padding-left: 130px !important; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/reset-password.component.html b/src/ui_ng/src/app/account/password/reset-password.component.html new file mode 100644 index 000000000..49386ba17 --- /dev/null +++ b/src/ui_ng/src/app/account/password/reset-password.component.html @@ -0,0 +1,50 @@ + + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/password/reset-password.component.ts b/src/ui_ng/src/app/account/password/reset-password.component.ts new file mode 100644 index 000000000..48bc17878 --- /dev/null +++ b/src/ui_ng/src/app/account/password/reset-password.component.ts @@ -0,0 +1,156 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, ViewChild, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { NgForm } from '@angular/forms'; + +import { PasswordSettingService } from './password-setting.service'; +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; +import { CommonRoutes } from '../../shared/shared.const'; + +@Component({ + selector: 'reset-password', + templateUrl: "reset-password.component.html", + styleUrls: ['password.component.css', '../../common.css'] +}) +export class ResetPasswordComponent implements OnInit { + opened: boolean = true; + private onGoing: boolean = false; + private password: string = ""; + private validationState: any = { + "newPassword": true, + "reNewPassword": true + }; + private resetUuid: string = ""; + private resetOk: boolean = false; + confirmPwd: string = ""; + + @ViewChild("resetPwdForm") resetPwdForm: NgForm; + @ViewChild(InlineAlertComponent) + private inlineAlert: InlineAlertComponent; + + constructor( + private pwdService: PasswordSettingService, + private route: ActivatedRoute, + private msgHandler: MessageHandlerService, + private router: Router) { } + + ngOnInit(): void { + this.route.queryParams.subscribe(params => this.resetUuid = params["reset_uuid"] || ""); + } + + public get showProgress(): boolean { + return this.onGoing; + } + + public get isValid(): boolean { + return this.resetPwdForm && this.resetPwdForm.valid && this.samePassword(); + } + + public get btnCancelCaption(): string { + if (!this.resetOk) { + return 'BUTTON.CANCEL'; + } else { + return 'BUTTON.CLOSE'; + } + } + + public getValidationState(key: string): boolean { + return this.validationState && + this.validationState[key]; + } + + public open(): void { + this.resetOk = false; + this.onGoing = false; + this.validationState = { + "newPassword": true, + "reNewPassword": true + }; + this.resetPwdForm.resetForm(); + this.inlineAlert.close(); + + this.opened = true; + } + + public close(): void { + //If already reset password ok, navigator to sign-in + if (this.resetOk) { + this.router.navigateByUrl(CommonRoutes.EMBEDDED_SIGN_IN); + } + this.opened = false; + } + + public send(): void { + //Double confirm to avoid improper situations + if (!this.password) { + return; + } + + if (!this.isValid) { + return; + } + + this.onGoing = true; + this.pwdService.resetPassword(this.resetUuid, this.password) + .then(() => { + this.onGoing = false; + this.resetOk = true; + this.inlineAlert.showInlineSuccess({ message: 'RESET_PWD.RESET_OK' }); + }) + .catch(error => { + this.onGoing = false; + if (this.msgHandler.isAppLevel(error)) { + this.close(); + } else { + this.inlineAlert.showInlineError(error); + } + }); + } + + public handleValidation(key: string, flag: boolean): void { + if (!flag) { + this.validationState[key] = true; + } else { + this.validationState[key] = this.getControlValidationState(key); + if (this.validationState[key]) { + this.validationState["reNewPassword"] = this.samePassword(); + } + } + } + + private getControlValidationState(key: string): boolean { + if (this.resetPwdForm) { + let control = this.resetPwdForm.controls[key]; + if (control) { + return control.valid; + } + } + + return false; + } + + private samePassword(): boolean { + if (this.resetPwdForm) { + let control1 = this.resetPwdForm.controls["newPassword"]; + let control2 = this.resetPwdForm.controls["reNewPassword"]; + if (control1 && control2) { + return control1.value == control2.value; + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-in/sign-in.component.css b/src/ui_ng/src/app/account/sign-in/sign-in.component.css new file mode 100644 index 000000000..b8dbc9ab8 --- /dev/null +++ b/src/ui_ng/src/app/account/sign-in/sign-in.component.css @@ -0,0 +1,44 @@ +.progress-size-small { + height: 0.5em !important; +} + +.visibility-hidden { + visibility: hidden; +} + +.forgot-password-link { + position: relative; + line-height: 36px; + font-size: 14px; + float: right; + top: -5px; +} + +.popular-repo-wrapper { + background-color: white; + min-height: 100vh; + display: flex; + flex-grow: 1; + margin-top: 24px; + justify-content: center; + flex-direction: column; + height: auto; + position: relative; + margin-left: 1px; +} + +.login-wrapper-override { + flex-wrap: wrap; +} + +.repo-container { + width: 100%; + margin-top: -250px; +} + +.more-info-link { + position: relative; + top: 80px; + left: 294px; + padding-right: 36px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-in/sign-in.component.html b/src/ui_ng/src/app/account/sign-in/sign-in.component.html new file mode 100644 index 000000000..ae4415aa4 --- /dev/null +++ b/src/ui_ng/src/app/account/sign-in/sign-in.component.html @@ -0,0 +1,44 @@ + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-in/sign-in.component.ts b/src/ui_ng/src/app/account/sign-in/sign-in.component.ts new file mode 100644 index 000000000..18712016b --- /dev/null +++ b/src/ui_ng/src/app/account/sign-in/sign-in.component.ts @@ -0,0 +1,254 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { Input, ViewChild, AfterViewChecked } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { SessionService } from '../../shared/session.service'; +import { SignInCredential } from '../../shared/sign-in-credential'; + +import { SignUpComponent } from '../sign-up/sign-up.component'; +import { CommonRoutes } from '../../shared/shared.const'; +import { ForgotPasswordComponent } from '../password/forgot-password.component'; + +import { AppConfigService } from '../../app-config.service'; +import { AppConfig } from '../../app-config'; +import { User } from '../../user/user'; + +import { CookieService, CookieOptions } from 'angular2-cookie/core'; + +//Define status flags for signing in states +export const signInStatusNormal = 0; +export const signInStatusOnGoing = 1; +export const signInStatusError = -1; +const remCookieKey = "rem-username"; +const expireDays = 10; + +@Component({ + selector: 'sign-in', + templateUrl: "sign-in.component.html", + styleUrls: ['sign-in.component.css'] +}) + +export class SignInComponent implements AfterViewChecked, OnInit { + private redirectUrl: string = ""; + private appConfig: AppConfig = new AppConfig(); + //Remeber me indicator + private rememberMe: boolean = false; + private rememberedName: string = ""; + //Form reference + signInForm: NgForm; + @ViewChild('signInForm') currentForm: NgForm; + @ViewChild('signupDialog') signUpDialog: SignUpComponent; + @ViewChild('forgotPwdDialog') forgotPwdDialog: ForgotPasswordComponent; + + //Status flag + signInStatus: number = signInStatusNormal; + + //Initialize sign in credential + @Input() signInCredential: SignInCredential = { + principal: "", + password: "" + }; + + constructor( + private router: Router, + private session: SessionService, + private route: ActivatedRoute, + private appConfigService: AppConfigService, + private cookie: CookieService + ) { } + + ngOnInit(): void { + //Make sure the updated configuration can be loaded + this.appConfigService.load() + .then(updatedConfig => this.appConfig = updatedConfig); + this.route.queryParams + .subscribe(params => { + this.redirectUrl = params["redirect_url"] || ""; + let isSignUp = params["sign_up"] || ""; + if (isSignUp != "") { + this.signUp();//Open sign up + } + }); + + let remUsername = this.cookie.get(remCookieKey); + remUsername = remUsername ? remUsername.trim() : ""; + if (remUsername) { + this.signInCredential.principal = remUsername; + this.rememberMe = true; + this.rememberedName = remUsername; + } + } + + //App title + public get appTitle(): string { + if (this.appConfig && this.appConfig.with_admiral) { + return "APP_TITLE.VIC"; + } + + return "APP_TITLE.VMW_HARBOR"; + } + + //For template accessing + public get isError(): boolean { + return this.signInStatus === signInStatusError; + } + + public get isOnGoing(): boolean { + return this.signInStatus === signInStatusOnGoing; + } + + //Validate the related fields + public get isValid(): boolean { + return this.currentForm.form.valid; + } + + //Whether show the 'sign up' link + public get selfSignUp(): boolean { + return this.appConfig.auth_mode === 'db_auth' + && this.appConfig.self_registration; + } + + public get showForgetPwd(): boolean { + return this.appConfig.auth_mode != 'ldap_auth'; + } + + private clickRememberMe($event): void { + if ($event && $event.target) { + this.rememberMe = $event.target.checked; + if (!this.rememberMe) { + //Remove cookie data + this.cookie.remove(remCookieKey); + this.rememberedName = ""; + } + } + } + + private remeberMe(): void { + if (this.rememberMe) { + if (this.rememberedName != this.signInCredential.principal) { + //Set expire time + let expires: number = expireDays * 3600 * 24 * 1000; + let date = new Date(Date.now() + expires); + let cookieptions = new CookieOptions({ + path: "/", + expires: date + }); + this.cookie.put(remCookieKey, this.signInCredential.principal, cookieptions); + } + } + } + + //General error handler + private handleError(error) { + //Set error status + this.signInStatus = signInStatusError; + + let message = error.status ? error.status + ":" + error.statusText : error; + console.error("An error occurred when signing in:", message); + } + + //Hande form values changes + private formChanged() { + if (this.currentForm === this.signInForm) { + return; + } + this.signInForm = this.currentForm; + if (this.signInForm) { + this.signInForm.valueChanges + .subscribe(data => { + this.updateState(); + }); + } + + } + + //Fill the new user info into the sign in form + private handleUserCreation(user: User): void { + if (user) { + this.currentForm.setValue({ + "login_username": user.username, + "login_password": "" + }); + + } + } + + //Implement interface + //Watch the view change only when view is in error state + ngAfterViewChecked() { + if (this.signInStatus === signInStatusError) { + this.formChanged(); + } + } + + //Update the status if we have done some changes + updateState(): void { + if (this.signInStatus === signInStatusError) { + this.signInStatus = signInStatusNormal; //reset + } + } + + //Trigger the signin action + signIn(): void { + //Should validate input firstly + if (!this.isValid) { + //Set error status + this.signInStatus = signInStatusError; + return; + } + + if (this.isOnGoing) { + //Ongoing, directly return + return; + } + + //Start signing in progress + this.signInStatus = signInStatusOnGoing; + + //Call the service to send out the http request + this.session.signIn(this.signInCredential) + .then(() => { + //Set status + //Keep it ongoing to keep the button 'disabled' + //this.signInStatus = signInStatusNormal; + + //Remeber me + this.remeberMe(); + + //Redirect to the right route + if (this.redirectUrl === "") { + //Routing to the default location + this.router.navigateByUrl(CommonRoutes.HARBOR_DEFAULT); + } else { + this.router.navigateByUrl(this.redirectUrl); + } + }) + .catch(error => { + this.handleError(error); + }); + } + + //Open sign up dialog + signUp(): void { + this.signUpDialog.open(); + } + + //Open forgot password dialog + forgotPassword(): void { + this.forgotPwdDialog.open(); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-in/sign-in.service.ts b/src/ui_ng/src/app/account/sign-in/sign-in.service.ts new file mode 100644 index 000000000..672b864a0 --- /dev/null +++ b/src/ui_ng/src/app/account/sign-in/sign-in.service.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, URLSearchParams } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { SignInCredential } from '../../shared/sign-in-credential'; + +const signInUrl = '/login'; +/** + * + * Define a service to provide sign in methods + * + * @export + * @class SignInService + */ +@Injectable() +export class SignInService { + private headers = new Headers({ + "Content-Type": 'application/x-www-form-urlencoded' + }); + + constructor(private http: Http) {} + + //Handle the related exceptions + private handleError(error: any): Promise{ + return Promise.reject(error.message || error); + } + + //Submit signin form to backend (NOT restful service) + signIn(signInCredential: SignInCredential): Promise{ + //Build the form package + const body = new URLSearchParams(); + body.set('principal', signInCredential.principal); + body.set('password', signInCredential.password); + + //Trigger Http + return this.http.post(signInUrl, body.toString(), { headers: this.headers }) + .toPromise() + .then(()=>null) + .catch(this.handleError); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-up/sign-up-page.component.html b/src/ui_ng/src/app/account/sign-up/sign-up-page.component.html new file mode 100644 index 000000000..bf6ecd161 --- /dev/null +++ b/src/ui_ng/src/app/account/sign-up/sign-up-page.component.html @@ -0,0 +1,7 @@ + + +
+ + + +
\ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-up/sign-up-page.component.ts b/src/ui_ng/src/app/account/sign-up/sign-up-page.component.ts new file mode 100644 index 000000000..53c9b28da --- /dev/null +++ b/src/ui_ng/src/app/account/sign-up/sign-up-page.component.ts @@ -0,0 +1,110 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Output, ViewChild, OnInit } from '@angular/core'; +import { NgForm } from '@angular/forms'; +import { Router } from '@angular/router'; + +import { NewUserFormComponent } from '../../shared/new-user-form/new-user-form.component'; +import { User } from '../../user/user'; + +import { UserService } from '../../user/user.service'; +import { errorHandler } from '../../shared/shared.utils'; +import { AlertType } from '../../shared/shared.const'; + +import { MessageService } from '../../global-message/message.service'; + +@Component({ + selector: 'sign-up-page', + templateUrl: "sign-up-page.component.html" +}) +export class SignUpPageComponent implements OnInit { + private error: any; + private onGoing: boolean = false; + private formValueChanged: boolean = false; + + constructor( + private userService: UserService, + private msgService: MessageService, + private router: Router) { } + + @ViewChild(NewUserFormComponent) + private newUserForm: NewUserFormComponent; + + private getNewUser(): User { + return this.newUserForm.getData(); + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public get isValid(): boolean { + return this.newUserForm.isValid && this.error == null; + } + + public get canBeCancelled(): boolean { + return this.formValueChanged && this.newUserForm && !this.newUserForm.isEmpty(); + } + + ngOnInit(): void { + this.newUserForm.reset();//Reset form + this.formValueChanged = false; + } + + formValueChange(flag: boolean): void { + if (flag) { + this.formValueChanged = true; + } + if (this.error != null) { + this.error = null;//clear error + } + } + + cancel(): void { + if (this.newUserForm) { + this.newUserForm.reset(); + } + } + + //Create new user + create(): void { + //Double confirm everything is ok + //Form is valid + if (!this.isValid) { + return; + } + + //We have new user data + let u = this.getNewUser(); + if (!u) { + return; + } + + //Start process + this.onGoing = true; + + this.userService.addUser(u) + .then(() => { + this.onGoing = false; + this.msgService.announceMessage(200, "", AlertType.SUCCESS); + //Navigate to embeded sign-in + this.router.navigate(['harbor', 'sign-in']); + }) + .catch(error => { + this.onGoing = false; + this.error = error + this.msgService.announceMessage(error.status | 500, "", AlertType.WARNING); + }); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-up/sign-up.component.html b/src/ui_ng/src/app/account/sign-up/sign-up.component.html new file mode 100644 index 000000000..c467fd45b --- /dev/null +++ b/src/ui_ng/src/app/account/sign-up/sign-up.component.html @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/account/sign-up/sign-up.component.ts b/src/ui_ng/src/app/account/sign-up/sign-up.component.ts new file mode 100644 index 000000000..f29900d3e --- /dev/null +++ b/src/ui_ng/src/app/account/sign-up/sign-up.component.ts @@ -0,0 +1,135 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Output, ViewChild, EventEmitter } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { NewUserFormComponent } from '../../shared/new-user-form/new-user-form.component'; +import { User } from '../../user/user'; + +import { SessionService } from '../../shared/session.service'; +import { UserService } from '../../user/user.service'; +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; + +import { Modal } from 'clarity-angular'; + +@Component({ + selector: 'sign-up', + templateUrl: "sign-up.component.html" +}) +export class SignUpComponent { + opened: boolean = false; + staticBackdrop: boolean = true; + private error: any; + private onGoing: boolean = false; + private formValueChanged: boolean = false; + + @Output() userCreation = new EventEmitter(); + + constructor( + private session: SessionService, + private userService: UserService) { } + + @ViewChild(NewUserFormComponent) + private newUserForm: NewUserFormComponent; + + @ViewChild(InlineAlertComponent) + private inlienAlert: InlineAlertComponent; + + @ViewChild(Modal) + private modal: Modal; + + private getNewUser(): User { + return this.newUserForm.getData(); + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public get isValid(): boolean { + return this.newUserForm.isValid && this.error == null; + } + + formValueChange(flag: boolean): void { + if (flag) { + this.formValueChanged = true; + } + if (this.error != null) { + this.error = null;//clear error + } + this.inlienAlert.close();//Close alert if being shown + } + + open(): void { + //Reset state + this.newUserForm.reset(); + this.formValueChanged = false; + this.error = null; + this.onGoing = false; + this.inlienAlert.close(); + + this.modal.open(); + } + + close(): void { + if (this.formValueChanged) { + if (this.newUserForm.isEmpty()) { + this.opened = false; + } else { + //Need user confirmation + this.inlienAlert.showInlineConfirmation({ + message: "ALERT.FORM_CHANGE_CONFIRMATION" + }); + } + } else { + this.opened = false; + } + } + + confirmCancel(): void { + this.opened = false; + this.modal.close(); + } + + //Create new user + create(): void { + //Double confirm everything is ok + //Form is valid + if (!this.isValid) { + return; + } + + //We have new user data + let u = this.getNewUser(); + if (!u) { + return; + } + + //Start process + this.onGoing = true; + + this.userService.addUser(u) + .then(() => { + this.onGoing = false; + this.opened = false; + this.modal.close(); + this.userCreation.emit(u); + }) + .catch(error => { + this.onGoing = false; + this.error = error; + this.inlienAlert.showInlineError(error); + }); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/app-config.service.ts b/src/ui_ng/src/app/app-config.service.ts new file mode 100644 index 000000000..fce8998e3 --- /dev/null +++ b/src/ui_ng/src/app/app-config.service.ts @@ -0,0 +1,96 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { AppConfig } from './app-config'; +import { CookieService } from 'angular2-cookie/core'; +import { CookieKeyOfAdmiral, HarborQueryParamKey } from './shared/shared.const'; +import { maintainUrlQueryParmas } from './shared/shared.utils'; + +export const systemInfoEndpoint = "/api/systeminfo"; +/** + * Declare service to handle the bootstrap options + * + * + * @export + * @class GlobalSearchService + */ +@Injectable() +export class AppConfigService { + private headers = new Headers({ + "Content-Type": 'application/json' + }); + private options = new RequestOptions({ + headers: this.headers + }); + + //Store the application configuration + private configurations: AppConfig = new AppConfig(); + + constructor( + private http: Http, + private cookie: CookieService) { } + + public load(): Promise { + return this.http.get(systemInfoEndpoint, this.options).toPromise() + .then(response => { + this.configurations = response.json() as AppConfig; + + //Read admiral endpoint from cookie if existing + let admiralUrlFromCookie: string = this.cookie.get(CookieKeyOfAdmiral); + if(admiralUrlFromCookie){ + //Override the endpoint from configuration file + this.configurations.admiral_endpoint = decodeURIComponent(admiralUrlFromCookie); + } + + return this.configurations; + }) + .catch(error => { + //Catch the error + console.error("Failed to load bootstrap options with error: ", error); + }); + } + + public getConfig(): AppConfig { + return this.configurations; + } + + public isIntegrationMode(): boolean { + return this.configurations && + this.configurations.with_admiral && + this.configurations.admiral_endpoint.trim() != ""; + } + + //Return the reconstructed admiral url + public getAdmiralEndpoint(currentHref: string): string { + let admiralUrl:string = this.configurations.admiral_endpoint; + if(admiralUrl.trim() === "" || currentHref.trim() === ""){ + return "#"; + } + + return maintainUrlQueryParmas(admiralUrl, HarborQueryParamKey, encodeURIComponent(currentHref)); + } + + public saveAdmiralEndpoint(endpoint: string): void { + if(!(endpoint.trim())){ + return; + } + + //Save back to cookie + this.cookie.put(CookieKeyOfAdmiral, endpoint); + this.configurations.admiral_endpoint = endpoint; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/app-config.ts b/src/ui_ng/src/app/app-config.ts new file mode 100644 index 000000000..a3166745b --- /dev/null +++ b/src/ui_ng/src/app/app-config.ts @@ -0,0 +1,37 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export class AppConfig { + constructor(){ + //Set default value + this.with_notary = false; + this.with_admiral = false; + this.admiral_endpoint = ""; + this.auth_mode = "db_auth"; + this.registry_url = ""; + this.project_creation_restriction = "everyone"; + this.self_registration = true; + this.has_ca_root = false; + this.harbor_version = "0.5.0";//default + } + + with_notary: boolean; + with_admiral: boolean; + admiral_endpoint: string; + auth_mode: string; + registry_url: string; + project_creation_restriction: string; + self_registration: boolean; + has_ca_root: boolean; + harbor_version: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/app.component.html b/src/ui_ng/src/app/app.component.html new file mode 100644 index 000000000..90c6b6463 --- /dev/null +++ b/src/ui_ng/src/app/app.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/ui_ng/src/app/app.component.scss b/src/ui_ng/src/app/app.component.scss new file mode 100644 index 000000000..7077d42f7 --- /dev/null +++ b/src/ui_ng/src/app/app.component.scss @@ -0,0 +1,8 @@ +// Copyright (c) 2016 VMware, Inc. All Rights Reserved. +// This software is released under MIT license. +// The full license information can be found in LICENSE in the root directory of this project. +.clr-icon { + &.clr-clarity-logo { + background-image: url(../images/clarity_logo.svg); + } +} diff --git a/src/ui_ng/src/app/app.component.spec.ts b/src/ui_ng/src/app/app.component.spec.ts new file mode 100644 index 000000000..498a42976 --- /dev/null +++ b/src/ui_ng/src/app/app.component.spec.ts @@ -0,0 +1,59 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* tslint:disable:no-unused-variable */ + +// import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +// import { AppComponent } from './app.component'; +// import { HomeComponent } from "./home/home.component"; +// import { AboutComponent } from "./about/about.component"; +// import { ClarityModule } from "clarity-angular"; +// import { ROUTING } from "./app.routing"; +// import { APP_BASE_HREF } from "@angular/common"; + +// describe('AppComponent', () => { + +// let fixture: ComponentFixture; +// let compiled: any; + +// beforeEach(() => { +// TestBed.configureTestingModule({ +// declarations: [ +// AppComponent, +// AboutComponent, +// HomeComponent +// ], +// imports: [ +// ClarityModule, +// ROUTING +// ], +// providers: [{provide: APP_BASE_HREF, useValue: '/'}] +// }); + +// fixture = TestBed.createComponent(AppComponent); +// fixture.detectChanges(); +// compiled = fixture.nativeElement; + + +// }); + +// afterEach(() => { +// fixture.destroy(); +// }); + +// it('should create the app', async(() => { +// expect(compiled).toBeTruthy(); +// })); + + +// }); diff --git a/src/ui_ng/src/app/app.component.ts b/src/ui_ng/src/app/app.component.ts new file mode 100644 index 000000000..b80c566d4 --- /dev/null +++ b/src/ui_ng/src/app/app.component.ts @@ -0,0 +1,65 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, ReflectiveInjector, LOCALE_ID } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CookieService } from 'angular2-cookie/core'; + +import { supportedLangs, enLang } from './shared/shared.const'; +import { SessionService } from './shared/session.service'; +import { AppConfigService } from './app-config.service'; +import { Title } from '@angular/platform-browser'; + +@Component({ + selector: 'harbor-app', + templateUrl: 'app.component.html' +}) +export class AppComponent { + constructor( + private translate: TranslateService, + private cookie: CookieService, + private session: SessionService, + private appConfigService: AppConfigService, + private titleService: Title) { + + translate.addLangs(supportedLangs); + translate.setDefaultLang(enLang); + + //If user has selected lang, then directly use it + let langSetting = this.cookie.get("harbor-lang"); + if (!langSetting || langSetting.trim() === "") { + //Use browser lang + langSetting = translate.getBrowserCultureLang().toLowerCase(); + } + + let selectedLang = this.isLangMatch(langSetting, supportedLangs) ? langSetting : enLang; + translate.use(selectedLang); + //this.session.switchLanguage(selectedLang).catch(error => console.error(error)); + + //Override page title + let key: string = "APP_TITLE.HARBOR"; + if (this.appConfigService.isIntegrationMode()) { + key = "APP_TITLE.REG"; + } + + translate.get(key).subscribe((res: string) => { + this.titleService.setTitle(res); + }); + } + + private isLangMatch(browserLang: string, supportedLangs: string[]) { + if (supportedLangs && supportedLangs.length > 0) { + return supportedLangs.find(lang => lang === browserLang); + } + } +} diff --git a/src/ui_ng/src/app/app.module.ts b/src/ui_ng/src/app/app.module.ts new file mode 100644 index 000000000..caa9e47c7 --- /dev/null +++ b/src/ui_ng/src/app/app.module.ts @@ -0,0 +1,84 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule, APP_INITIALIZER, LOCALE_ID } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { ClarityModule } from 'clarity-angular'; +import { AppComponent } from './app.component'; + +import { BaseModule } from './base/base.module'; +import { HarborRoutingModule } from './harbor-routing.module'; +import { SharedModule } from './shared/shared.module'; +import { AccountModule } from './account/account.module'; +import { ConfigurationModule } from './config/config.module'; + +import { TranslateModule, TranslateLoader, TranslateService, MissingTranslationHandler } from "@ngx-translate/core"; +import { MyMissingTranslationHandler } from './i18n/missing-trans.handler'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; +import { Http } from '@angular/http'; + +import { AppConfigService } from './app-config.service'; + +export function HttpLoaderFactory(http: Http) { + return new TranslateHttpLoader(http, 'i18n/lang/', '-lang.json'); +} + +export function initConfig(configService: AppConfigService) { + return () => configService.load(); +} + +export function getCurrentLanguage(translateService: TranslateService) { + return translateService.currentLang; +} + +@NgModule({ + declarations: [ + AppComponent, + ], + imports: [ + SharedModule, + BaseModule, + AccountModule, + HarborRoutingModule, + ConfigurationModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: (HttpLoaderFactory), + deps: [Http] + }, + missingTranslationHandler: { + provide: MissingTranslationHandler, + useClass: MyMissingTranslationHandler + } + }) + ], + providers: [ + AppConfigService, + { + provide: APP_INITIALIZER, + useFactory: initConfig, + deps: [ AppConfigService ], + multi: true + }, + { + provide: LOCALE_ID, + useFactory: getCurrentLanguage, + deps:[ TranslateService ] + } + ], + bootstrap: [AppComponent] +}) +export class AppModule {} diff --git a/src/ui_ng/src/app/base/base.module.ts b/src/ui_ng/src/app/base/base.module.ts new file mode 100644 index 000000000..3f8ff83a6 --- /dev/null +++ b/src/ui_ng/src/app/base/base.module.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { RouterModule } from '@angular/router'; + +import { ProjectModule } from '../project/project.module'; +import { UserModule } from '../user/user.module'; +import { AccountModule } from '../account/account.module'; +import { RepositoryModule } from '../repository/repository.module'; + +import { NavigatorComponent } from './navigator/navigator.component'; +import { GlobalSearchComponent } from './global-search/global-search.component'; +import { FooterComponent } from './footer/footer.component'; +import { HarborShellComponent } from './harbor-shell/harbor-shell.component'; +import { SearchResultComponent } from './global-search/search-result.component'; +import { StartPageComponent } from './start-page/start.component'; + +import { SearchTriggerService } from './global-search/search-trigger.service'; + +@NgModule({ + imports: [ + SharedModule, + ProjectModule, + UserModule, + AccountModule, + RouterModule, + RepositoryModule + ], + declarations: [ + NavigatorComponent, + GlobalSearchComponent, + FooterComponent, + HarborShellComponent, + SearchResultComponent, + StartPageComponent + ], + exports: [ HarborShellComponent ], + providers: [SearchTriggerService] +}) +export class BaseModule { + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/footer/footer.component.html b/src/ui_ng/src/app/base/footer/footer.component.html new file mode 100644 index 000000000..e69de29bb diff --git a/src/ui_ng/src/app/base/footer/footer.component.ts b/src/ui_ng/src/app/base/footer/footer.component.ts new file mode 100644 index 000000000..5ebe6fbfd --- /dev/null +++ b/src/ui_ng/src/app/base/footer/footer.component.ts @@ -0,0 +1,23 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'footer', + templateUrl: "footer.component.html" +}) +export class FooterComponent { + // constructor(private router: Router){} +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/global-search.component.html b/src/ui_ng/src/app/base/global-search/global-search.component.html new file mode 100644 index 000000000..d6848dd47 --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/global-search.component.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/global-search.component.ts b/src/ui_ng/src/app/base/global-search/global-search.component.ts new file mode 100644 index 000000000..6e68f025a --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/global-search.component.ts @@ -0,0 +1,82 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; +import { Router } from '@angular/router'; +import { Subject } from 'rxjs/Subject'; +import { Observable } from 'rxjs/Observable'; +import { Subscription } from 'rxjs/Subscription'; + +import { SearchTriggerService } from './search-trigger.service'; + +import { AppConfigService } from '../../app-config.service'; + +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; + +const deBounceTime = 500; //ms + +@Component({ + selector: 'global-search', + templateUrl: "global-search.component.html" +}) +export class GlobalSearchComponent implements OnInit, OnDestroy { + //Keep search term as Subject + private searchTerms = new Subject(); + + //Keep subscription for future use + private searchSub: Subscription; + private closeSub: Subscription; + + //To indicate if the result panel is opened + private isResPanelOpened: boolean = false; + private searchTerm: string = ""; + + //Placeholder text + placeholderText: string = "GLOBAL_SEARCH.PLACEHOLDER"; + + constructor( + private searchTrigger: SearchTriggerService, + private router: Router, + private appConfigService: AppConfigService) { } + + //Implement ngOnIni + ngOnInit(): void { + this.searchSub = this.searchTerms + .debounceTime(deBounceTime) + //.distinctUntilChanged() + .subscribe(term => { + this.searchTrigger.triggerSearch(term); + }); + this.closeSub = this.searchTrigger.searchClearChan$.subscribe(clear => { + this.searchTerm = ""; + }); + + if(this.appConfigService.isIntegrationMode()){ + this.placeholderText = "GLOBAL_SEARCH.PLACEHOLDER_VIC"; + } + } + + ngOnDestroy(): void { + if (this.searchSub) { + this.searchSub.unsubscribe(); + } + } + + //Handle the term inputting event + search(term: string): void { + //Send event even term is empty + + this.searchTerms.next(term.trim()); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/global-search.service.ts b/src/ui_ng/src/app/base/global-search/global-search.service.ts new file mode 100644 index 000000000..1ef70b3fc --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/global-search.service.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { SearchResults } from './search-results'; + +const searchEndpoint = "/api/search"; +/** + * Declare service to handle the global search + * + * + * @export + * @class GlobalSearchService + */ +@Injectable() +export class GlobalSearchService { + private headers = new Headers({ + "Content-Type": 'application/json' + }); + private options = new RequestOptions({ + headers: this.headers + }); + + constructor(private http: Http) { } + + /** + * Search related artifacts with the provided keyword + * + * @param {string} keyword + * @returns {Promise} + * + * @memberOf GlobalSearchService + */ + doSearch(term: string): Promise { + let searchUrl = searchEndpoint + "?q=" + term; + + return this.http.get(searchUrl, this.options).toPromise() + .then(response => response.json() as SearchResults) + .catch(error => Promise.reject(error)); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/search-result.component.css b/src/ui_ng/src/app/base/global-search/search-result.component.css new file mode 100644 index 000000000..b92d46761 --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/search-result.component.css @@ -0,0 +1,56 @@ +.search-overlay { + display: block; + position: fixed; + height: 100%; + width: 100%; + /*shoud be lesser than 1000 to aoivd override the popup menu*/ + z-index: 999; + box-sizing: border-box; + background: #fafafa; + top: 60px; + left: 0px; + padding-left: 36px; + padding-right: 36px; +} + +.search-header { + display: inline-block; + width: 100%; + position: relative; +} + +.search-title { + font-size: 28px; + letter-spacing: normal; + color: #000; +} + +.search-close { + position: absolute; + right: 24px; + cursor: pointer; +} + +.search-parent-override { + position: relative !important; +} + +.search-spinner { + top: 50%; + left: 50%; + position: absolute; +} + +.grid-header-wrapper { + text-align: right; +} + +.grid-filter { + position: relative; + top: 8px; + margin: 0px auto 0px auto; +} + +.search-header a:hover { + text-decoration: none; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/search-result.component.html b/src/ui_ng/src/app/base/global-search/search-result.component.html new file mode 100644 index 000000000..00d68ca56 --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/search-result.component.html @@ -0,0 +1,14 @@ +
+
+ + +
{{'SEARCH.IN_PROGRESS' | translate}}
+
+

{{'PROJECT.PROJECTS' | translate}}

+ +

{{'PROJECT_DETAIL.REPOSITORIES' | translate}}

+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/search-result.component.ts b/src/ui_ng/src/app/base/global-search/search-result.component.ts new file mode 100644 index 000000000..2bf2d5fe2 --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/search-result.component.ts @@ -0,0 +1,147 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core'; + +import { GlobalSearchService } from './global-search.service'; +import { SearchResults } from './search-results'; +import { SearchTriggerService } from './search-trigger.service'; + +import { Subscription } from 'rxjs/Subscription'; + +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +@Component({ + selector: "search-result", + templateUrl: "search-result.component.html", + styleUrls: ["search-result.component.css"], + + providers: [GlobalSearchService] +}) + +export class SearchResultComponent implements OnInit, OnDestroy { + private searchResults: SearchResults = new SearchResults(); + private originalCopy: SearchResults; + + private currentTerm: string = ""; + + //Open or close + private stateIndicator: boolean = false; + //Search in progress + private onGoing: boolean = false; + + //Whether or not mouse point is onto the close indicator + private mouseOn: boolean = false; + + //Watch message channel + private searchSub: Subscription; + private closeSearchSub: Subscription; + + constructor( + private search: GlobalSearchService, + private msgHandler: MessageHandlerService, + private searchTrigger: SearchTriggerService) { } + + ngOnInit() { + this.searchSub = this.searchTrigger.searchTriggerChan$.subscribe(term => { + this.doSearch(term); + }); + this.closeSearchSub = this.searchTrigger.searchCloseChan$.subscribe(close => { + this.close(); + }); + } + + ngOnDestroy() { + if (this.searchSub) { + this.searchSub.unsubscribe(); + } + + if (this.closeSearchSub) { + this.closeSearchSub.unsubscribe(); + } + } + + private clone(src: SearchResults): SearchResults { + let res: SearchResults = new SearchResults(); + + if (src) { + src.project.forEach(pro => res.project.push(Object.assign({}, pro))); + src.repository.forEach(repo => res.repository.push(Object.assign({}, repo))) + + return res; + } + + return res//Empty object + } + + public get state(): boolean { + return this.stateIndicator; + } + + public get done(): boolean { + return !this.onGoing; + } + + public get hover(): boolean { + return this.mouseOn; + } + + //Show the results + show(): void { + this.stateIndicator = true; + } + + //Close the result page + close(): void { + this.stateIndicator = false; + this.searchTrigger.clear(true); + } + + //Call search service to complete the search request + doSearch(term: string): void { + //Only search none empty term + if (!term || term.trim() === "") { + return; + } + //Do nothing if search is ongoing + if (this.onGoing) { + return; + } + //Confirm page is displayed + if (!this.stateIndicator) { + this.show(); + } + + this.currentTerm = term; + + //If term is empty, then clear the results + if (term === "") { + this.searchResults.project = []; + this.searchResults.repository = []; + return; + } + //Show spinner + this.onGoing = true; + + this.search.doSearch(term) + .then(searchResults => { + this.onGoing = false; + this.originalCopy = searchResults; //Keeo the original data + this.searchResults = this.clone(searchResults); + }) + .catch(error => { + this.onGoing = false; + this.msgHandler.handleError(error); + }); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/search-results.ts b/src/ui_ng/src/app/base/global-search/search-results.ts new file mode 100644 index 000000000..a6740e854 --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/search-results.ts @@ -0,0 +1,25 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Project } from '../../project/project'; +import { Repository } from '../../repository/repository'; + +export class SearchResults { + constructor(){ + this.project = []; + this.repository = []; + } + + project: Project[]; + repository: Repository[]; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/global-search/search-trigger.service.ts b/src/ui_ng/src/app/base/global-search/search-trigger.service.ts new file mode 100644 index 000000000..eb41747ea --- /dev/null +++ b/src/ui_ng/src/app/base/global-search/search-trigger.service.ts @@ -0,0 +1,44 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { AlertType } from '../../shared/shared.const'; + +@Injectable() +export class SearchTriggerService { + + private searchTriggerSource = new Subject(); + private searchCloseSource = new Subject(); + private searchClearSource = new Subject(); + + searchTriggerChan$ = this.searchTriggerSource.asObservable(); + searchCloseChan$ = this.searchCloseSource.asObservable(); + searchClearChan$ = this.searchClearSource.asObservable(); + + triggerSearch(event: string) { + this.searchTriggerSource.next(event); + } + + //Set event to true for shell + //set to false for search panel + closeSearch(event: boolean) { + this.searchCloseSource.next(event); + } + + //Clear search term + clear(event): void { + this.searchClearSource.next(event); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.css b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.css new file mode 100644 index 000000000..bdd94c9b1 --- /dev/null +++ b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.css @@ -0,0 +1,28 @@ +.side-nav-override { + box-shadow: 6px 0px 0px 0px #ccc; +} + +.container-override { + position: relative !important; +} + +.start-content-padding { + padding: 0px !important; + background-color: white; + overflow-y: hidden !important; +} + +.content-area-override { + padding: 36px !important; + padding-right: 21px !important; +} + + +/*.global-message-alert { + position: fixed; + top: 55px; + left: 260px; + width: 100%; + z-index: 999; + padding-right: 276px; +}*/ \ No newline at end of file diff --git a/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.html b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.html new file mode 100644 index 000000000..2ae65c2be --- /dev/null +++ b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.html @@ -0,0 +1,33 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.ts b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.ts new file mode 100644 index 000000000..f4d05d7bb --- /dev/null +++ b/src/ui_ng/src/app/base/harbor-shell/harbor-shell.component.ts @@ -0,0 +1,122 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; + +import { ModalEvent } from '../modal-event'; +import { modalEvents } from '../modal-events.const'; + +import { AccountSettingsModalComponent } from '../../account/account-settings/account-settings-modal.component'; +import { PasswordSettingComponent } from '../../account/password/password-setting.component'; +import { NavigatorComponent } from '../navigator/navigator.component'; +import { SessionService } from '../../shared/session.service'; + +import { AboutDialogComponent } from '../../shared/about-dialog/about-dialog.component'; + +import { SearchTriggerService } from '../global-search/search-trigger.service'; + +import { Subscription } from 'rxjs/Subscription'; + +import { CommonRoutes } from '../../shared/shared.const'; + +@Component({ + selector: 'harbor-shell', + templateUrl: 'harbor-shell.component.html', + styleUrls: ["harbor-shell.component.css"] +}) + +export class HarborShellComponent implements OnInit, OnDestroy { + + @ViewChild(AccountSettingsModalComponent) + private accountSettingsModal: AccountSettingsModalComponent; + + @ViewChild(PasswordSettingComponent) + private pwdSetting: PasswordSettingComponent; + + @ViewChild(NavigatorComponent) + private navigator: NavigatorComponent; + + @ViewChild(AboutDialogComponent) + private aboutDialog: AboutDialogComponent; + + //To indicator whwther or not the search results page is displayed + //We need to use this property to do some overriding work + private isSearchResultsOpened: boolean = false; + + private searchSub: Subscription; + private searchCloseSub: Subscription; + + constructor( + private route: ActivatedRoute, + private router: Router, + private session: SessionService, + private searchTrigger: SearchTriggerService) { } + + ngOnInit() { + this.searchSub = this.searchTrigger.searchTriggerChan$.subscribe(searchEvt => { + if(searchEvt && searchEvt.trim() != ""){ + this.isSearchResultsOpened = true; + } + }); + + this.searchCloseSub = this.searchTrigger.searchCloseChan$.subscribe(close => { + this.isSearchResultsOpened = false; + }); + } + + ngOnDestroy(): void { + if (this.searchSub) { + this.searchSub.unsubscribe(); + } + + if (this.searchCloseSub) { + this.searchCloseSub.unsubscribe(); + } + } + + public get shouldOverrideContent(): boolean { + return this.router.routerState.snapshot.url.toString().startsWith(CommonRoutes.EMBEDDED_SIGN_IN); + } + + public get showSearch(): boolean { + return this.isSearchResultsOpened; + } + + public get isSystemAdmin(): boolean { + let account = this.session.getCurrentUser(); + return account != null && account.has_admin_role > 0; + } + + public get isUserExisting(): boolean { + let account = this.session.getCurrentUser(); + return account != null; + } + + //Open modal dialog + openModal(event: ModalEvent): void { + switch (event.modalName) { + case modalEvents.USER_PROFILE: + this.accountSettingsModal.open(); + break; + case modalEvents.CHANGE_PWD: + this.pwdSetting.open(); + break; + case modalEvents.ABOUT: + this.aboutDialog.open(); + break; + default: + break; + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/modal-event.ts b/src/ui_ng/src/app/base/modal-event.ts new file mode 100644 index 000000000..7c53a9c28 --- /dev/null +++ b/src/ui_ng/src/app/base/modal-event.ts @@ -0,0 +1,18 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//Define a object to store the modal event +export class ModalEvent { + modalName: string; + modalFlag: boolean; //true for open, false for close +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/modal-events.const.ts b/src/ui_ng/src/app/base/modal-events.const.ts new file mode 100644 index 000000000..d34afc08c --- /dev/null +++ b/src/ui_ng/src/app/base/modal-events.const.ts @@ -0,0 +1,18 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export const modalEvents = { + USER_PROFILE: "USER_PROFILE", + CHANGE_PWD: "CHANGE_PWD", + ABOUT: "ABOUT" +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/navigator/navigator.component.css b/src/ui_ng/src/app/base/navigator/navigator.component.css new file mode 100644 index 000000000..3b658d839 --- /dev/null +++ b/src/ui_ng/src/app/base/navigator/navigator.component.css @@ -0,0 +1,31 @@ +.sign-in-override { + padding-left: 0px !important; + padding-right: 5px !important; +} + +.sign-up-override { + padding-left: 5px !important; +} + +.custom-divider { + display: inline-block; + border-right: 2px inset snow; + padding: 2px 0px 2px 0px; + vertical-align: middle; + height: 24px; +} + +.lang-selected { + font-weight: bold; +} + +.nav-divider { + display: inline-block; + width: 1px; + height: 40px; + background-color: #fafafa; + position: relative; + top: 10px; + opacity: 0.15; + content: ''; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/navigator/navigator.component.html b/src/ui_ng/src/app/base/navigator/navigator.component.html new file mode 100644 index 000000000..27b555f2a --- /dev/null +++ b/src/ui_ng/src/app/base/navigator/navigator.component.html @@ -0,0 +1,42 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/base/navigator/navigator.component.ts b/src/ui_ng/src/app/base/navigator/navigator.component.ts new file mode 100644 index 000000000..0800638e5 --- /dev/null +++ b/src/ui_ng/src/app/base/navigator/navigator.component.ts @@ -0,0 +1,170 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Output, EventEmitter, OnInit } from '@angular/core'; +import { Router, NavigationExtras } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; + +import { ModalEvent } from '../modal-event'; +import { modalEvents } from '../modal-events.const'; + +import { SessionUser } from '../../shared/session-user'; +import { SessionService } from '../../shared/session.service'; +import { CookieService, CookieOptions } from 'angular2-cookie/core'; + +import { supportedLangs, enLang, languageNames, CommonRoutes } from '../../shared/shared.const'; +import { AppConfigService } from '../../app-config.service'; +import { SearchTriggerService } from '../global-search/search-trigger.service'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +@Component({ + selector: 'navigator', + templateUrl: "navigator.component.html", + styleUrls: ["navigator.component.css"] +}) + +export class NavigatorComponent implements OnInit { + // constructor(private router: Router){} + @Output() showAccountSettingsModal = new EventEmitter(); + @Output() showPwdChangeModal = new EventEmitter(); + + private selectedLang: string = enLang; + private appTitle: string = 'APP_TITLE.HARBOR'; + + constructor( + private session: SessionService, + private router: Router, + private translate: TranslateService, + private cookie: CookieService, + private appConfigService: AppConfigService, + private msgHandler: MessageHandlerService, + private searchTrigger: SearchTriggerService) { + + } + + ngOnInit(): void { + this.selectedLang = this.translate.currentLang; + this.translate.onLangChange.subscribe(langChange => { + this.selectedLang = langChange.lang; + //Keep in cookie for next use + let opt = new CookieOptions({path: '/', expires: new Date(Date.now() + 3600*1000*24*31)}); + this.cookie.put("harbor-lang", langChange.lang, opt); + }); + if (this.appConfigService.isIntegrationMode()) { + this.appTitle = 'APP_TITLE.VIC'; + } + } + + public get isSessionValid(): boolean { + return this.session.getCurrentUser() != null; + } + + public get accountName(): string { + return this.session.getCurrentUser() ? this.session.getCurrentUser().username : "N/A"; + } + + public get currentLang(): string { + return languageNames[this.selectedLang]; + } + + public get admiralLink(): string { + return this.appConfigService.getAdmiralEndpoint(window.location.href); + } + + public get isIntegrationMode(): boolean { + return this.appConfigService.isIntegrationMode(); + } + + public get canDownloadCert(): boolean { + return this.session.getCurrentUser() && + this.session.getCurrentUser().has_admin_role > 0 && + this.appConfigService.getConfig() && + this.appConfigService.getConfig().has_ca_root; + } + + public get canChangePassword(): boolean { + return this.session.getCurrentUser() && + this.appConfigService.getConfig() && + this.appConfigService.getConfig().auth_mode != 'ldap_auth'; + } + + matchLang(lang: string): boolean { + return lang.trim() === this.selectedLang; + } + + //Open the account setting dialog + openAccountSettingsModal(): void { + this.showAccountSettingsModal.emit({ + modalName: modalEvents.USER_PROFILE, + modalFlag: true + }); + } + + //Open change password dialog + openChangePwdModal(): void { + this.showPwdChangeModal.emit({ + modalName: modalEvents.CHANGE_PWD, + modalFlag: true + }); + } + + //Open about dialog + openAboutDialog(): void { + this.showPwdChangeModal.emit({ + modalName: modalEvents.ABOUT, + modalFlag: true + }); + } + + //Log out system + logOut(): void { + //Naviagte to the sign in route + //Appending 'signout' means destroy session cache + let navigatorExtra: NavigationExtras = { + queryParams: { "signout": true } + }; + this.router.navigate([CommonRoutes.EMBEDDED_SIGN_IN], navigatorExtra); + //Confirm search result panel is close + this.searchTrigger.closeSearch(true); + } + + //Switch languages + switchLanguage(lang: string): void { + let selectedLang: string = enLang;//Default + if (supportedLangs.find(supportedLang => supportedLang === lang.trim())) { + selectedLang = lang; + } else { + console.error('Language ' + lang.trim() + ' is not suppoted yet'); + } + + this.translate.use(selectedLang).subscribe(() => window.location.reload()); + } + + //Handle the home action + homeAction(): void { + if (this.session.getCurrentUser() != null) { + //Navigate to default page + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + } else { + //Naviagte to signin page + this.router.navigate([CommonRoutes.HARBOR_ROOT]); + } + + //Confirm search result panel is close + this.searchTrigger.closeSearch(true); + } + + registryAction(): void { + this.searchTrigger.closeSearch(true); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/start-page/start.component.css b/src/ui_ng/src/app/base/start-page/start.component.css new file mode 100644 index 000000000..f63f87286 --- /dev/null +++ b/src/ui_ng/src/app/base/start-page/start.component.css @@ -0,0 +1,30 @@ +.start-card { + border-right: 1px solid #cccccc; + padding: 24px; + background-color: white; + height: 100%; +} + +.row-fill-height { + height: 100%; +} + +.row-margin { + margin-left: 24px; +} + +.column-fill-height { + height: 100%; +} + +.my-card-img { + background-image: url('../../../images/harbor-logo.png'); + background-repeat: no-repeat; + background-size: contain; + height: 160px; +} + +.my-card-footer { + float: right; + margin-top: 100px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/base/start-page/start.component.html b/src/ui_ng/src/app/base/start-page/start.component.html new file mode 100644 index 000000000..f246dc02b --- /dev/null +++ b/src/ui_ng/src/app/base/start-page/start.component.html @@ -0,0 +1,29 @@ + +
+
+ + +
+
+ + +
+
+
+
+
+
+

Getting Start

+

+ {{'START_PAGE.GETTING_START' | translate}} +

+
+ +
+
+
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/base/start-page/start.component.ts b/src/ui_ng/src/app/base/start-page/start.component.ts new file mode 100644 index 000000000..b6a2eb7e8 --- /dev/null +++ b/src/ui_ng/src/app/base/start-page/start.component.ts @@ -0,0 +1,34 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit } from '@angular/core'; + +import { SessionService } from '../../shared/session.service'; +import { SessionUser } from '../../shared/session-user'; + +@Component({ + selector: 'start-page', + templateUrl: "start.component.html", + styleUrls: ['start.component.css'] +}) +export class StartPageComponent implements OnInit{ + private isSessionValid: boolean = false; + + constructor( + private session: SessionService + ) { } + + ngOnInit(): void { + this.isSessionValid = this.session.getCurrentUser() != null; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/common.css b/src/ui_ng/src/app/common.css new file mode 100644 index 000000000..ba425eec5 --- /dev/null +++ b/src/ui_ng/src/app/common.css @@ -0,0 +1,17 @@ +.form-group-override { + padding-left: 170px !important; +} + +.form-group-label-override { + font-size: 14px; + font-weight: 400; +} + +.sub-label-for-input { + position: absolute; + top: 26px; + font-size: 10px; + font-weight: 400; + line-height: 12px; + left: 170px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/auth/config-auth.component.html b/src/ui_ng/src/app/config/auth/config-auth.component.html new file mode 100644 index 000000000..8a9d81a36 --- /dev/null +++ b/src/ui_ng/src/app/config/auth/config-auth.component.html @@ -0,0 +1,141 @@ +
+
+
+ +
+ +
+ + + {{'CONFIG.TOOLTIP.AUTH_MODE' | translate}} + +
+
+
+
+ + +
+
+ + + + + {{'CONFIG.TOOLTIP.LDAP_SEARCH_DN' | translate}} + +
+
+ + +
+
+ + + + + {{'CONFIG.TOOLTIP.LDAP_BASE_DN' | translate}} + +
+
+ + +
+
+ + + + + {{'CONFIG.TOOLTIP.LDAP_UID' | translate}} + +
+
+ +
+ +
+ + + {{'CONFIG.TOOLTIP.LDAP_SCOPE' | translate}} + +
+
+
+
+ +
+ +
+ + + {{'CONFIG.TOOLTIP.PRO_CREATION_RESTRICTION' | translate}} + +
+
+ + + + + {{'CONFIG.TOOLTIP.SELF_REGISTRATION' | translate}} + + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/auth/config-auth.component.ts b/src/ui_ng/src/app/config/auth/config-auth.component.ts new file mode 100644 index 000000000..ff5397995 --- /dev/null +++ b/src/ui_ng/src/app/config/auth/config-auth.component.ts @@ -0,0 +1,65 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, ViewChild } from '@angular/core'; +import { NgForm } from '@angular/forms'; +import { Subscription } from 'rxjs/Subscription'; + +import { Configuration } from '../config'; + +@Component({ + selector: 'config-auth', + templateUrl: "config-auth.component.html", + styleUrls: ['../config.component.css'] +}) +export class ConfigurationAuthComponent { + private changeSub: Subscription; + @Input("ldapConfig") currentConfig: Configuration = new Configuration(); + + @ViewChild("authConfigFrom") authForm: NgForm; + + constructor() { } + + public get showLdap(): boolean { + return this.currentConfig && + this.currentConfig.auth_mode && + this.currentConfig.auth_mode.value === 'ldap_auth'; + } + + public get showSelfReg(): boolean { + if (!this.currentConfig || !this.currentConfig.auth_mode) { + return true; + } else { + return this.currentConfig.auth_mode.value != 'ldap_auth'; + } + } + + private disabled(prop: any): boolean { + return !(prop && prop.editable); + } + + public isValid(): boolean { + return this.authForm && this.authForm.valid; + } + + private handleOnChange($event): void { + if ($event && $event.target && $event.target["value"]) { + let authMode = $event.target["value"]; + if (authMode === 'ldap_auth') { + if (this.currentConfig.self_registration.value) { + this.currentConfig.self_registration.value = false;//uncheck + } + } + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.component.1.html b/src/ui_ng/src/app/config/config.component.1.html new file mode 100644 index 000000000..a4be64f0c --- /dev/null +++ b/src/ui_ng/src/app/config/config.component.1.html @@ -0,0 +1,62 @@ +
+

{{'CONFIG.TITLE' | translate }}

+ + + {{'CONFIG.AUTH' | translate }} + {{'CONFIG.REPLICATION' | translate }} + {{'CONFIG.EMAIL' | translate }} + {{'CONFIG.SYSTEM' | translate }} + + + + + +
+
+
+ + + + + {{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate }} + + +
+
+
+
+ + + + +
+
+
+ + + + + {{'CONFIG.TOOLTIP.TOKEN_EXPIRATION' | translate}} + +
+
+
+
+
+
+ + + + + +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.component.css b/src/ui_ng/src/app/config/config.component.css new file mode 100644 index 000000000..d8e00e3d2 --- /dev/null +++ b/src/ui_ng/src/app/config/config.component.css @@ -0,0 +1,11 @@ +.custom-h2 { + margin-top: 0px !important; +} + +.info-tips-icon { + color: grey; +} + +.info-tips-icon:hover { + color: #007CBB; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.component.html b/src/ui_ng/src/app/config/config.component.html new file mode 100644 index 000000000..65aef9879 --- /dev/null +++ b/src/ui_ng/src/app/config/config.component.html @@ -0,0 +1,72 @@ +
+
+

{{'CONFIG.TITLE' | translate }}

+ + +
+ +
+
+
+
+
+ + + + + {{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate }} + + +
+
+
+
+
+ +
+
+
+
+
+ + + + + {{'CONFIG.TOOLTIP.TOKEN_EXPIRATION' | translate}} + +
+
+
+
+
+ + + + + + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.component.ts b/src/ui_ng/src/app/config/config.component.ts new file mode 100644 index 000000000..091adfa30 --- /dev/null +++ b/src/ui_ng/src/app/config/config.component.ts @@ -0,0 +1,470 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgForm } from '@angular/forms'; + +import { ConfigurationService } from './config.service'; +import { Configuration } from './config'; +import { ConfirmationTargets, ConfirmationState } from '../shared/shared.const';; +import { StringValueItem } from './config'; +import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service'; +import { Subscription } from 'rxjs/Subscription'; +import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message' + +import { ConfigurationAuthComponent } from './auth/config-auth.component'; +import { ConfigurationEmailComponent } from './email/config-email.component'; + +import { AppConfigService } from '../app-config.service'; +import { SessionService } from '../shared/session.service'; +import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; + +const fakePass = "aWpLOSYkIzJTTU4wMDkx"; +const TabLinkContentMap = { + "config-auth": "authentication", + "config-replication": "replication", + "config-email": "email", + "config-system": "system_settings" +}; + +@Component({ + selector: 'config', + templateUrl: "config.component.html", + styleUrls: ['config.component.css'] +}) +export class ConfigurationComponent implements OnInit, OnDestroy { + private onGoing: boolean = false; + allConfig: Configuration = new Configuration(); + private currentTabId: string = "config-auth";//default tab + private originalCopy: Configuration; + private confirmSub: Subscription; + private testingMailOnGoing: boolean = false; + private testingLDAPOnGoing: boolean = false; + + @ViewChild("repoConfigFrom") repoConfigForm: NgForm; + @ViewChild("systemConfigFrom") systemConfigForm: NgForm; + @ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent; + @ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent; + + constructor( + private msgHandler: MessageHandlerService, + private configService: ConfigurationService, + private confirmService: ConfirmationDialogService, + private appConfigService: AppConfigService, + private session: SessionService) { } + + private isCurrentTabLink(tabId: string): boolean { + return this.currentTabId === tabId; + } + + private isCurrentTabContent(contentId: string): boolean { + return TabLinkContentMap[this.currentTabId] === contentId; + } + + private hasUnsavedChangesOfCurrentTab(): any { + let allChanges = this.getChanges(); + if (this.isEmpty(allChanges)) { + return null; + } + + let properties = []; + switch (this.currentTabId) { + case "config-auth": + for (let prop in allChanges) { + if (prop.startsWith("ldap_")) { + return allChanges; + } + } + properties = ["auth_mode", "project_creation_restriction", "self_registration"]; + break; + case "config-email": + for (let prop in allChanges) { + if (prop.startsWith("email_")) { + return allChanges; + } + } + return null; + case "config-replication": + properties = ["verify_remote_cert"]; + break; + case "config-system": + properties = ["token_expiration"]; + break; + default: + return null; + } + + for (let prop in allChanges) { + if (properties.indexOf(prop) != -1) { + return allChanges; + } + } + + return null; + } + + ngOnInit(): void { + //First load + //Double confirm the current use has admin role + let currentUser = this.session.getCurrentUser(); + if (currentUser && currentUser.has_admin_role > 0) { + this.retrieveConfig(); + } + + this.confirmSub = this.confirmService.confirmationConfirm$.subscribe(confirmation => { + if (confirmation && + confirmation.state === ConfirmationState.CONFIRMED) { + if (confirmation.source === ConfirmationTargets.CONFIG) { + this.reset(confirmation.data); + } else if (confirmation.source === ConfirmationTargets.CONFIG_TAB) { + this.reset(confirmation.data["changes"]); + this.currentTabId = confirmation.data["tabId"]; + } + } + }); + } + + ngOnDestroy(): void { + if (this.confirmSub) { + this.confirmSub.unsubscribe(); + } + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public isValid(): boolean { + return this.repoConfigForm && + this.repoConfigForm.valid && + this.systemConfigForm && + this.systemConfigForm.valid && + this.mailConfig && + this.mailConfig.isValid() && + this.authConfig && + this.authConfig.isValid(); + } + + public hasChanges(): boolean { + return !this.isEmpty(this.getChanges()); + } + + public isMailConfigValid(): boolean { + return this.mailConfig && + this.mailConfig.isValid() && + !this.testingMailOnGoing; + } + + public get showTestServerBtn(): boolean { + return this.currentTabId === 'config-email'; + } + + public get showLdapServerBtn(): boolean { + return this.currentTabId === 'config-auth' && + this.allConfig.auth_mode && + this.allConfig.auth_mode.value === "ldap_auth"; + } + + public get hideMailTestingSpinner(): boolean { + return !this.testingMailOnGoing || !this.showTestServerBtn; + } + + public get hideLDAPTestingSpinner(): boolean { + return !this.testingLDAPOnGoing || !this.showLdapServerBtn; + } + + public isLDAPConfigValid(): boolean { + return this.authConfig && + this.authConfig.isValid() && + !this.testingLDAPOnGoing; + } + + public tabLinkClick(tabLink: string) { + //Whether has unsave changes in current tab + let changes = this.hasUnsavedChangesOfCurrentTab(); + if (!changes) { + this.currentTabId = tabLink; + return; + } + + this.confirmUnsavedTabChanges(changes, tabLink); + } + + /** + * + * Save the changed values + * + * @memberOf ConfigurationComponent + */ + public save(): void { + let changes = this.getChanges(); + if (!this.isEmpty(changes)) { + this.onGoing = true; + this.configService.saveConfiguration(changes) + .then(response => { + this.onGoing = false; + //API should return the updated configurations here + //Unfortunately API does not do that + //To refresh the view, we can clone the original data copy + //or force refresh by calling service. + //HERE we choose force way + this.retrieveConfig(); + + //Reload bootstrap option + this.appConfigService.load().catch(error => console.error("Failed to reload bootstrap option with error: ", error)); + + this.msgHandler.showSuccess("CONFIG.SAVE_SUCCESS"); + }) + .catch(error => { + this.onGoing = false; + this.msgHandler.handleError(error); + }); + } else { + //Inprop situation, should not come here + console.error("Save obort becasue nothing changed"); + } + } + + /** + * + * Discard current changes if have and reset + * + * @memberOf ConfigurationComponent + */ + public cancel(): void { + let changes = this.getChanges(); + if (!this.isEmpty(changes)) { + this.confirmUnsavedChanges(changes); + } else { + //Inprop situation, should not come here + console.error("Nothing changed"); + } + } + + /** + * + * Test the connection of specified mail server + * + * + * @memberOf ConfigurationComponent + */ + public testMailServer(): void { + if(this.testingMailOnGoing){ + return;//Should not come here + } + let mailSettings = {}; + for (let prop in this.allConfig) { + if (prop.startsWith("email_")) { + mailSettings[prop] = this.allConfig[prop].value; + } + } + //Confirm port is number + mailSettings["email_port"] = +mailSettings["email_port"]; + let allChanges = this.getChanges(); + let password = allChanges["email_password"] + if (password) { + mailSettings["email_password"] = password; + } else { + delete mailSettings["email_password"]; + } + + this.testingMailOnGoing = true; + this.configService.testMailServer(mailSettings) + .then(response => { + this.testingMailOnGoing = false; + this.msgHandler.showSuccess("CONFIG.TEST_MAIL_SUCCESS"); + }) + .catch(error => { + this.testingMailOnGoing = false; + let err = error._body; + if (!err) { + err = "UNKNOWN"; + } + this.msgHandler.showError("CONFIG.TEST_MAIL_FAILED", { 'param': err }); + }); + } + + public testLDAPServer(): void { + if(this.testingLDAPOnGoing){ + return;//Should not come here + } + + let ldapSettings = {}; + for (let prop in this.allConfig) { + if (prop.startsWith("ldap_")) { + ldapSettings[prop] = this.allConfig[prop].value; + } + } + + let allChanges = this.getChanges(); + let ldapSearchPwd = allChanges["ldap_search_password"]; + if (ldapSearchPwd) { + ldapSettings['ldap_search_password'] = ldapSearchPwd; + } else { + delete ldapSettings['ldap_search_password']; + } + + this.testingLDAPOnGoing = true; + this.configService.testLDAPServer(ldapSettings) + .then(respone => { + this.testingLDAPOnGoing = false; + this.msgHandler.showSuccess("CONFIG.TEST_LDAP_SUCCESS"); + }) + .catch(error => { + this.testingLDAPOnGoing = false; + let err = error._body; + if (!err) { + err = "UNKNOWN"; + } + this.msgHandler.showError("CONFIG.TEST_LDAP_FAILED", { 'param': err }); + }); + } + + private confirmUnsavedChanges(changes: any) { + let msg = new ConfirmationMessage( + "CONFIG.CONFIRM_TITLE", + "CONFIG.CONFIRM_SUMMARY", + "", + changes, + ConfirmationTargets.CONFIG + ); + + this.confirmService.openComfirmDialog(msg); + } + + private confirmUnsavedTabChanges(changes: any, tabId: string) { + let msg = new ConfirmationMessage( + "CONFIG.CONFIRM_TITLE", + "CONFIG.CONFIRM_SUMMARY", + "", + { + "changes": changes, + "tabId": tabId + }, + ConfirmationTargets.CONFIG_TAB + ); + + this.confirmService.openComfirmDialog(msg); + } + + private retrieveConfig(): void { + this.onGoing = true; + this.configService.getConfiguration() + .then(configurations => { + this.onGoing = false; + + //Add two password fields + configurations.email_password = new StringValueItem(fakePass, true); + configurations.ldap_search_password = new StringValueItem(fakePass, true); + this.allConfig = configurations; + + //Keep the original copy of the data + this.originalCopy = this.clone(configurations); + }) + .catch(error => { + this.onGoing = false; + this.msgHandler.handleError(error); + }); + } + + /** + * + * Get the changed fields and return a map + * + * @private + * @returns {*} + * + * @memberOf ConfigurationComponent + */ + private getChanges(): any { + let changes = {}; + if (!this.allConfig || !this.originalCopy) { + return changes; + } + + for (let prop in this.allConfig) { + let field = this.originalCopy[prop]; + if (field && field.editable) { + if (field.value != this.allConfig[prop].value) { + changes[prop] = this.allConfig[prop].value; + //Fix boolean issue + if (typeof field.value === "boolean") { + changes[prop] = changes[prop] ? "1" : "0"; + } + } + } + } + + return changes; + } + + /** + * + * Deep clone the configuration object + * + * @private + * @param {Configuration} src + * @returns {Configuration} + * + * @memberOf ConfigurationComponent + */ + private clone(src: Configuration): Configuration { + let dest = new Configuration(); + if (!src) { + return dest;//Empty + } + + for (let prop in src) { + if (src[prop]) { + dest[prop] = Object.assign({}, src[prop]); //Deep copy inner object + } + } + + return dest; + } + + /** + * + * Reset the configuration form + * + * @private + * @param {*} changes + * + * @memberOf ConfigurationComponent + */ + private reset(changes: any): void { + if (!this.isEmpty(changes)) { + for (let prop in changes) { + if (this.originalCopy[prop]) { + this.allConfig[prop] = Object.assign({}, this.originalCopy[prop]); + } + } + } else { + //force reset + this.retrieveConfig(); + } + } + + private isEmpty(obj) { + for (let key in obj) { + if (obj.hasOwnProperty(key)) + return false; + } + return true; + } + + private disabled(prop: any): boolean { + return !(prop && prop.editable); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.module.ts b/src/ui_ng/src/app/config/config.module.ts new file mode 100644 index 000000000..8e16832f6 --- /dev/null +++ b/src/ui_ng/src/app/config/config.module.ts @@ -0,0 +1,35 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { CoreModule } from '../core/core.module'; +import { SharedModule } from '../shared/shared.module'; + +import { ConfigurationComponent } from './config.component'; +import { ConfigurationService } from './config.service'; +import { ConfigurationAuthComponent } from './auth/config-auth.component'; +import { ConfigurationEmailComponent } from './email/config-email.component'; + +@NgModule({ + imports: [ + CoreModule, + SharedModule + ], + declarations: [ + ConfigurationComponent, + ConfigurationAuthComponent, + ConfigurationEmailComponent], + exports: [ConfigurationComponent], + providers: [ConfigurationService] +}) +export class ConfigurationModule { } \ No newline at end of file diff --git a/src/ui_ng/src/app/config/config.service.ts b/src/ui_ng/src/app/config/config.service.ts new file mode 100644 index 000000000..2c1761d64 --- /dev/null +++ b/src/ui_ng/src/app/config/config.service.ts @@ -0,0 +1,62 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { Configuration } from './config'; + +const configEndpoint = "/api/configurations"; +const emailEndpoint = "/api/email/ping"; +const ldapEndpoint = "/api/ldap/ping"; + +@Injectable() +export class ConfigurationService { + private headers: Headers = new Headers({ + "Accept": 'application/json', + "Content-Type": 'application/json' + }); + private options: RequestOptions = new RequestOptions({ + 'headers': this.headers + }); + + constructor(private http: Http) { } + + public getConfiguration(): Promise { + return this.http.get(configEndpoint, this.options).toPromise() + .then(response => response.json() as Configuration) + .catch(error => Promise.reject(error)); + } + + public saveConfiguration(values: any): Promise { + return this.http.put(configEndpoint, JSON.stringify(values), this.options) + .toPromise() + .then(response => response) + .catch(error => Promise.reject(error)); + } + + public testMailServer(mailSettings: any): Promise { + return this.http.post(emailEndpoint, JSON.stringify(mailSettings), this.options) + .toPromise() + .then(response => response) + .catch(error => Promise.reject(error)); + } + + public testLDAPServer(ldapSettings: any): Promise { + return this.http.post(ldapEndpoint, JSON.stringify(ldapSettings), this.options) + .toPromise() + .then(response => response) + .catch(error => Promise.reject(error)); + } +} diff --git a/src/ui_ng/src/app/config/config.ts b/src/ui_ng/src/app/config/config.ts new file mode 100644 index 000000000..b9b55ac8d --- /dev/null +++ b/src/ui_ng/src/app/config/config.ts @@ -0,0 +1,90 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export class StringValueItem { + value: string; + editable: boolean; + + public constructor(v: string, e: boolean) { + this.value = v; + this.editable = e; + } +} + +export class NumberValueItem { + value: number; + editable: boolean; + + public constructor(v: number, e: boolean) { + this.value = v; + this.editable = e; + } +} + +export class BoolValueItem { + value: boolean; + editable: boolean; + + public constructor(v: boolean, e: boolean) { + this.value = v; + this.editable = e; + } +} + +export class Configuration { + auth_mode: StringValueItem; + project_creation_restriction: StringValueItem; + self_registration: BoolValueItem; + ldap_base_dn: StringValueItem; + ldap_filter?: StringValueItem; + ldap_scope: NumberValueItem; + ldap_search_dn?: StringValueItem; + ldap_search_password?: StringValueItem; + ldap_timeout: NumberValueItem; + ldap_uid: StringValueItem; + ldap_url: StringValueItem; + email_host: StringValueItem; + email_identity: StringValueItem; + email_from: StringValueItem; + email_port: NumberValueItem; + email_ssl: BoolValueItem; + email_username?: StringValueItem; + email_password?: StringValueItem; + verify_remote_cert: BoolValueItem; + token_expiration: NumberValueItem; + cfg_expiration: NumberValueItem; + + public constructor() { + this.auth_mode = new StringValueItem("db_auth", true); + this.project_creation_restriction = new StringValueItem("everyone", true); + this.self_registration = new BoolValueItem(false, true); + this.ldap_base_dn = new StringValueItem("", true); + this.ldap_filter = new StringValueItem("", true); + this.ldap_scope = new NumberValueItem(0, true); + this.ldap_search_dn = new StringValueItem("", true); + this.ldap_search_password = new StringValueItem("", true); + this.ldap_timeout = new NumberValueItem(5, true); + this.ldap_uid = new StringValueItem("", true); + this.ldap_url = new StringValueItem("", true); + this.email_host = new StringValueItem("", true); + this.email_identity = new StringValueItem("", true); + this.email_from = new StringValueItem("", true); + this.email_port = new NumberValueItem(25, true); + this.email_ssl = new BoolValueItem(false, true); + this.email_username = new StringValueItem("", true); + this.email_password = new StringValueItem("", true); + this.token_expiration = new NumberValueItem(5, true); + this.cfg_expiration = new NumberValueItem(30, true); + this.verify_remote_cert = new BoolValueItem(false, true); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/config/email/config-email.component.html b/src/ui_ng/src/app/config/email/config-email.component.html new file mode 100644 index 000000000..ab24088b8 --- /dev/null +++ b/src/ui_ng/src/app/config/email/config-email.component.html @@ -0,0 +1,72 @@ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + + {{'CONFIG.SSL_TOOLTIP' | translate}} + + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/config/email/config-email.component.ts b/src/ui_ng/src/app/config/email/config-email.component.ts new file mode 100644 index 000000000..329790b9c --- /dev/null +++ b/src/ui_ng/src/app/config/email/config-email.component.ts @@ -0,0 +1,38 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, ViewChild } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { Configuration } from '../config'; + +@Component({ + selector: 'config-email', + templateUrl: "config-email.component.html", + styleUrls: ['../config.component.css'] +}) +export class ConfigurationEmailComponent { + @Input("mailConfig") currentConfig: Configuration = new Configuration(); + + @ViewChild("mailConfigFrom") mailForm: NgForm; + + constructor() { } + + private disabled(prop: any): boolean { + return !(prop && prop.editable); + } + + public isValid(): boolean { + return this.mailForm && this.mailForm.valid; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/core/core.module.ts b/src/ui_ng/src/app/core/core.module.ts new file mode 100644 index 000000000..aebfbe6ab --- /dev/null +++ b/src/ui_ng/src/app/core/core.module.ts @@ -0,0 +1,35 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { ClarityModule } from 'clarity-angular'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + HttpModule, + ClarityModule.forRoot() + ], + exports: [ + BrowserModule, + FormsModule, + HttpModule, + ClarityModule + ] +}) +export class CoreModule { +} diff --git a/src/ui_ng/src/app/global-message/message.component.html b/src/ui_ng/src/app/global-message/message.component.html new file mode 100644 index 000000000..2859e90a1 --- /dev/null +++ b/src/ui_ng/src/app/global-message/message.component.html @@ -0,0 +1,10 @@ + +
+ + {{message}} + +
+ +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/global-message/message.component.ts b/src/ui_ng/src/app/global-message/message.component.ts new file mode 100644 index 000000000..ddfa61bf3 --- /dev/null +++ b/src/ui_ng/src/app/global-message/message.component.ts @@ -0,0 +1,126 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { Router } from '@angular/router'; +import { Subscription } from 'rxjs/Subscription'; +import { TranslateService } from '@ngx-translate/core'; + +import { Message } from './message'; +import { MessageService } from './message.service'; + +import { AlertType, dismissInterval, httpStatusCode, CommonRoutes } from '../shared/shared.const'; + +@Component({ + selector: 'global-message', + templateUrl: 'message.component.html' +}) +export class MessageComponent implements OnInit, OnDestroy { + + @Input() isAppLevel: boolean; + globalMessage: Message = new Message(); + globalMessageOpened: boolean; + messageText: string = ""; + private timer: any = null; + + private appLevelMsgSub: Subscription; + private msgSub: Subscription; + private clearSub: Subscription; + + constructor( + private messageService: MessageService, + private router: Router, + private translate: TranslateService) { } + + ngOnInit(): void { + //Only subscribe application level message + if (this.isAppLevel) { + this.appLevelMsgSub = this.messageService.appLevelAnnounced$.subscribe( + message => { + this.globalMessageOpened = true; + this.globalMessage = message; + this.messageText = message.message; + + this.translateMessage(message); + } + ) + } else { + //Only subscribe general messages + this.msgSub = this.messageService.messageAnnounced$.subscribe( + message => { + this.globalMessageOpened = true; + this.globalMessage = message; + this.messageText = message.message; + + this.translateMessage(message); + + // Make the message alert bar dismiss after several intervals. + //Only for this case + this.timer = setTimeout(() => this.onClose(), dismissInterval); + } + ); + } + + this.clearSub = this.messageService.clearChan$.subscribe(clear => { + this.onClose(); + }); + } + + ngOnDestroy() { + if (this.appLevelMsgSub) { + this.appLevelMsgSub.unsubscribe(); + } + + if (this.msgSub) { + this.msgSub.unsubscribe(); + } + + if (this.clearSub) { + this.clearSub.unsubscribe(); + } + } + + //Translate or refactor the message shown to user + translateMessage(msg: Message): void { + let key = "UNKNOWN_ERROR", param = ""; + if (msg && msg.message) { + key = (typeof msg.message === "string" ? msg.message.trim() : msg.message); + if (key === "") { + key = "UNKNOWN_ERROR"; + } + } + + this.translate.get(key, { 'param': param }).subscribe((res: string) => this.messageText = res); + } + + public get needAuth(): boolean { + return this.globalMessage ? + this.globalMessage.statusCode === httpStatusCode.Unauthorized : false; + } + + //Show message text + public get message(): string { + return this.messageText; + } + + signIn(): void { + this.router.navigateByUrl(CommonRoutes.EMBEDDED_SIGN_IN); + } + + onClose() { + if (this.timer) { + clearTimeout(this.timer); + } + this.globalMessageOpened = false; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/global-message/message.service.ts b/src/ui_ng/src/app/global-message/message.service.ts new file mode 100644 index 000000000..d9a292787 --- /dev/null +++ b/src/ui_ng/src/app/global-message/message.service.ts @@ -0,0 +1,41 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { Message } from './message'; +import { AlertType } from '../shared/shared.const'; + +@Injectable() +export class MessageService { + + private messageAnnouncedSource = new Subject(); + private appLevelAnnouncedSource = new Subject(); + private clearSource = new Subject(); + + messageAnnounced$ = this.messageAnnouncedSource.asObservable(); + appLevelAnnounced$ = this.appLevelAnnouncedSource.asObservable(); + clearChan$ = this.clearSource.asObservable(); + + announceMessage(statusCode: number, message: string, alertType: AlertType) { + this.messageAnnouncedSource.next(Message.newMessage(statusCode, message, alertType)); + } + + announceAppLevelMessage(statusCode: number, message: string, alertType: AlertType) { + this.appLevelAnnouncedSource.next(Message.newMessage(statusCode, message, alertType)); + } + + clear() { + this.clearSource.next(true); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/global-message/message.ts b/src/ui_ng/src/app/global-message/message.ts new file mode 100644 index 000000000..22a24cdba --- /dev/null +++ b/src/ui_ng/src/app/global-message/message.ts @@ -0,0 +1,53 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { AlertType } from '../shared/shared.const'; + +export class Message { + statusCode: number; + message: string; + alertType: AlertType; + isAppLevel: boolean = false; + + get type(): string { + switch (this.alertType) { + case AlertType.DANGER: + return 'alert-danger'; + case AlertType.INFO: + return 'alert-info'; + case AlertType.SUCCESS: + return 'alert-success'; + case AlertType.WARNING: + return 'alert-warning'; + default: + return 'alert-warning'; + } + } + + constructor() { } + + static newMessage(statusCode: number, message: string, alertType: AlertType): Message { + let m = new Message(); + m.statusCode = statusCode; + m.message = message; + m.alertType = alertType; + return m; + } + + + toString(): string { + return 'Message with statusCode:' + this.statusCode + + ', message:' + this.message + + ', alert type:' + this.type; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/harbor-routing.module.ts b/src/ui_ng/src/app/harbor-routing.module.ts new file mode 100644 index 000000000..f5eb5d075 --- /dev/null +++ b/src/ui_ng/src/app/harbor-routing.module.ts @@ -0,0 +1,147 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; + +import { RouterModule, Routes } from '@angular/router'; + +import { SignInComponent } from './account/sign-in/sign-in.component'; +import { HarborShellComponent } from './base/harbor-shell/harbor-shell.component'; +import { ProjectComponent } from './project/project.component'; +import { UserComponent } from './user/user.component'; +import { ReplicationManagementComponent } from './replication/replication-management/replication-management.component'; + +import { TotalReplicationComponent } from './replication/total-replication/total-replication.component'; +import { DestinationComponent } from './replication/destination/destination.component'; + +import { ProjectDetailComponent } from './project/project-detail/project-detail.component'; + +import { RepositoryComponent } from './repository/repository.component'; +import { TagRepositoryComponent } from './repository/tag-repository/tag-repository.component'; +import { ReplicationComponent } from './replication/replication.component'; +import { MemberComponent } from './project/member/member.component'; +import { AuditLogComponent } from './log/audit-log.component'; + +import { ProjectRoutingResolver } from './project/project-routing-resolver.service'; +import { SystemAdminGuard } from './shared/route/system-admin-activate.service'; +import { SignUpComponent } from './account/sign-up/sign-up.component'; +import { ResetPasswordComponent } from './account/password/reset-password.component'; +import { RecentLogComponent } from './log/recent-log.component'; +import { ConfigurationComponent } from './config/config.component'; +import { PageNotFoundComponent } from './shared/not-found/not-found.component' +import { StartPageComponent } from './base/start-page/start.component'; +import { SignUpPageComponent } from './account/sign-up/sign-up-page.component'; + +import { AuthCheckGuard } from './shared/route/auth-user-activate.service'; +import { SignInGuard } from './shared/route/sign-in-guard-activate.service'; +import { LeavingConfigRouteDeactivate } from './shared/route/leaving-config-deactivate.service'; + +import { MemberGuard } from './shared/route/member-guard-activate.service'; + +const harborRoutes: Routes = [ + { path: '', redirectTo: 'harbor', pathMatch: 'full' }, + { path: 'reset_password', component: ResetPasswordComponent }, + { + path: 'harbor', + component: HarborShellComponent, + canActivateChild: [AuthCheckGuard], + children: [ + { path: '', redirectTo: 'sign-in', pathMatch: 'full' }, + { + path: 'sign-in', + component: SignInComponent, + canActivate: [SignInGuard] + }, + { + path: 'projects', + component: ProjectComponent + }, + { + path: 'logs', + component: RecentLogComponent + }, + { + path: 'users', + component: UserComponent, + canActivate: [SystemAdminGuard] + }, + { + path: 'replications', + component: ReplicationManagementComponent, + canActivate: [SystemAdminGuard], + canActivateChild: [SystemAdminGuard], + children: [ + { + path: 'rules', + component: TotalReplicationComponent + }, + { + path: 'endpoints', + component: DestinationComponent + } + ] + }, + { + path: 'tags/:id/:repo', + component: TagRepositoryComponent, + resolve: { + projectResolver: ProjectRoutingResolver + } + }, + { + path: 'projects/:id', + component: ProjectDetailComponent, + canActivate: [MemberGuard], + resolve: { + projectResolver: ProjectRoutingResolver + }, + children: [ + { + path: 'repository', + component: RepositoryComponent + }, + { + path: 'replication', + component: ReplicationComponent, + canActivate: [SystemAdminGuard] + }, + { + path: 'member', + component: MemberComponent + }, + { + path: 'log', + component: AuditLogComponent + } + ] + }, + { + path: 'configs', + component: ConfigurationComponent, + canActivate: [SystemAdminGuard], + canDeactivate: [LeavingConfigRouteDeactivate] + } + ] + }, + { path: "**", component: PageNotFoundComponent } +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(harborRoutes) + ], + exports: [RouterModule] +}) +export class HarborRoutingModule { + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/i18n/missing-trans.handler.ts b/src/ui_ng/src/app/i18n/missing-trans.handler.ts new file mode 100644 index 000000000..73552efa9 --- /dev/null +++ b/src/ui_ng/src/app/i18n/missing-trans.handler.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { MissingTranslationHandler, MissingTranslationHandlerParams } from '@ngx-translate/core'; + +export class MyMissingTranslationHandler implements MissingTranslationHandler { + handle(params: MissingTranslationHandlerParams) { + const missingText = "{Miss Harbor Text}"; + return params.key || missingText; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/index.ts b/src/ui_ng/src/app/index.ts new file mode 100644 index 000000000..bc8c95fe4 --- /dev/null +++ b/src/ui_ng/src/app/index.ts @@ -0,0 +1,15 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export * from './app.component'; +export * from './app.module'; diff --git a/src/ui_ng/src/app/log/audit-log.component.css b/src/ui_ng/src/app/log/audit-log.component.css new file mode 100644 index 000000000..f2ead1afc --- /dev/null +++ b/src/ui_ng/src/app/log/audit-log.component.css @@ -0,0 +1,5 @@ +.option-right { + padding-right: 16px; + margin-top: 22px; + margin-bottom: 2px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/log/audit-log.component.html b/src/ui_ng/src/app/log/audit-log.component.html new file mode 100644 index 000000000..39e2b726e --- /dev/null +++ b/src/ui_ng/src/app/log/audit-log.component.html @@ -0,0 +1,46 @@ +
+
+
+
+ + + +
+
+
+ + + + +
+ + +
+
+
+
+ + {{'AUDIT_LOG.USERNAME' | translate}} + {{'AUDIT_LOG.REPOSITORY_NAME' | translate}} + {{'AUDIT_LOG.TAGS' | translate}} + {{'AUDIT_LOG.OPERATION' | translate}} + {{'AUDIT_LOG.TIMESTAMP' | translate}} + + {{l.username}} + {{l.repo_name}} + {{l.repo_tag}} + {{l.operation}} + {{l.op_time | date: 'short'}} + + + {{totalRecordCount}} {{'AUDIT_LOG.ITEMS' | translate}} + + + +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/log/audit-log.component.ts b/src/ui_ng/src/app/log/audit-log.component.ts new file mode 100644 index 000000000..a82521686 --- /dev/null +++ b/src/ui_ng/src/app/log/audit-log.component.ts @@ -0,0 +1,169 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Params, Router } from '@angular/router'; + +import { AuditLog } from './audit-log'; +import { SessionUser } from '../shared/session-user'; + +import { AuditLogService } from './audit-log.service'; +import { SessionService } from '../shared/session.service'; +import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; +import { AlertType } from '../shared/shared.const'; + +import { State } from 'clarity-angular'; + +const optionalSearch: {} = {0: 'AUDIT_LOG.ADVANCED', 1: 'AUDIT_LOG.SIMPLE'}; + +class FilterOption { + key: string; + description: string; + checked: boolean; + + constructor(private iKey: string, private iDescription: string, private iChecked: boolean) { + this.key = iKey; + this.description = iDescription; + this.checked = iChecked; + } + + toString(): string { + return 'key:' + this.key + ', description:' + this.description + ', checked:' + this.checked + '\n'; + } +} + +@Component({ + selector: 'audit-log', + templateUrl: './audit-log.component.html', + styleUrls: [ './audit-log.component.css' ] +}) +export class AuditLogComponent implements OnInit { + + currentUser: SessionUser; + projectId: number; + queryParam: AuditLog = new AuditLog(); + auditLogs: AuditLog[]; + + toggleName = optionalSearch; + currentOption: number = 0; + filterOptions: FilterOption[] = [ + new FilterOption('all', 'AUDIT_LOG.ALL_OPERATIONS', true), + new FilterOption('pull', 'AUDIT_LOG.PULL', true), + new FilterOption('push', 'AUDIT_LOG.PUSH', true), + new FilterOption('create', 'AUDIT_LOG.CREATE', true), + new FilterOption('delete', 'AUDIT_LOG.DELETE', true), + new FilterOption('others', 'AUDIT_LOG.OTHERS', true) + ]; + + pageOffset: number = 1; + pageSize: number = 15; + totalRecordCount: number; + totalPage: number; + + constructor(private route: ActivatedRoute, private router: Router, private auditLogService: AuditLogService, private messageHandlerService: MessageHandlerService) { + //Get current user from registered resolver. + this.route.data.subscribe(data=>this.currentUser = data['auditLogResolver']); + } + + ngOnInit(): void { + this.projectId = +this.route.snapshot.parent.params['id']; + console.log('Get projectId from route params snapshot:' + this.projectId); + this.queryParam.project_id = this.projectId; + this.queryParam.page_size = this.pageSize; + } + + retrieve(state?: State): void { + if(state) { + this.queryParam.page = state.page.to + 1; + } + this.auditLogService + .listAuditLogs(this.queryParam) + .subscribe( + response=>{ + this.totalRecordCount = response.headers.get('x-total-count'); + this.totalPage = Math.ceil(this.totalRecordCount / this.pageSize); + console.log('TotalRecordCount:' + this.totalRecordCount + ', totalPage:' + this.totalPage); + this.auditLogs = response.json(); + }, + error=>{ + this.router.navigate(['/harbor', 'projects']); + this.messageHandlerService.handleError(error); + } + ); + } + + doSearchAuditLogs(searchUsername: string): void { + this.queryParam.username = searchUsername; + this.retrieve(); + } + + doSearchByTimeRange(strDate: string, target: string): void { + let oneDayOffset = 3600 * 24; + switch(target) { + case 'begin': + this.queryParam.begin_timestamp = new Date(strDate).getTime() / 1000; + break; + case 'end': + this.queryParam.end_timestamp = new Date(strDate).getTime() / 1000 + oneDayOffset; + break; + } + console.log('Search audit log filtered by time range, begin: ' + this.queryParam.begin_timestamp + ', end:' + this.queryParam.end_timestamp); + this.retrieve(); + } + + doSearchByOptions() { + let selectAll = true; + let operationFilter: string[] = []; + for(var i in this.filterOptions) { + let filterOption = this.filterOptions[i]; + if(filterOption.checked) { + operationFilter.push(this.filterOptions[i].key); + }else{ + selectAll = false; + } + } + if(selectAll) { + operationFilter = []; + } + this.queryParam.keywords = operationFilter.join('/'); + this.retrieve(); + console.log('Search option filter:' + operationFilter.join('/')); + } + + toggleOptionalName(option: number): void { + (option === 1) ? this.currentOption = 0 : this.currentOption = 1; + } + + toggleFilterOption(option: string): void { + let selectedOption = this.filterOptions.find(value =>(value.key === option)); + selectedOption.checked = !selectedOption.checked; + if(selectedOption.key === 'all') { + this.filterOptions.filter(value=> value.key !== selectedOption.key).forEach(value => value.checked = selectedOption.checked); + } else { + if(!selectedOption.checked) { + this.filterOptions.find(value=>value.key === 'all').checked = false; + } + let selectAll = true; + this.filterOptions.filter(value=> value.key !== 'all').forEach(value =>{ + if(!value.checked) { + selectAll = false; + } + }); + this.filterOptions.find(value=>value.key === 'all').checked = selectAll; + } + this.doSearchByOptions(); + } + refresh(): void { + this.retrieve(); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/log/audit-log.service.ts b/src/ui_ng/src/app/log/audit-log.service.ts new file mode 100644 index 000000000..c5c90182e --- /dev/null +++ b/src/ui_ng/src/app/log/audit-log.service.ts @@ -0,0 +1,56 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Http, Headers, RequestOptions } from '@angular/http'; + +import { AuditLog } from './audit-log'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/observable/throw'; + +export const logEndpoint = "/api/logs"; + +@Injectable() +export class AuditLogService { + private httpOptions = new RequestOptions({ + headers: new Headers({ + "Content-Type": 'application/json', + "Accept": 'application/json' + }) + }); + + constructor(private http: Http) {} + + listAuditLogs(queryParam: AuditLog): Observable { + return this.http + .post(`/api/projects/${queryParam.project_id}/logs/filter?page=${queryParam.page}&page_size=${queryParam.page_size}`, { + begin_timestamp: queryParam.begin_timestamp, + end_timestamp: queryParam.end_timestamp, + keywords: queryParam.keywords, + operation: queryParam.operation, + project_id: queryParam.project_id, + username: queryParam.username + }) + .map(response => response) + .catch(error => Observable.throw(error)); + } + + getRecentLogs(lines: number): Observable { + return this.http.get(logEndpoint + "?lines=" + lines, this.httpOptions) + .map(response => response.json() as AuditLog[]) + .catch(error => Observable.throw(error)); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/log/audit-log.ts b/src/ui_ng/src/app/log/audit-log.ts new file mode 100644 index 000000000..2a5cbfb3e --- /dev/null +++ b/src/ui_ng/src/app/log/audit-log.ts @@ -0,0 +1,45 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + { + "log_id": 3, + "user_id": 0, + "project_id": 0, + "repo_name": "library/mysql", + "repo_tag": "5.6", + "guid": "", + "operation": "push", + "op_time": "2017-02-14T09:22:58Z", + "username": "admin", + "keywords": "", + "BeginTime": "0001-01-01T00:00:00Z", + "begin_timestamp": 0, + "EndTime": "0001-01-01T00:00:00Z", + "end_timestamp": 0 + } +*/ +export class AuditLog { + log_id: number; + project_id: number; + username: string; + repo_name: string; + repo_tag: string; + operation: string; + op_time: Date; + begin_timestamp: number = 0; + end_timestamp: number = 0; + keywords: string; + page: number; + page_size: number; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/log/log.module.ts b/src/ui_ng/src/app/log/log.module.ts new file mode 100644 index 000000000..533e20d7d --- /dev/null +++ b/src/ui_ng/src/app/log/log.module.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { AuditLogComponent } from './audit-log.component'; +import { SharedModule } from '../shared/shared.module'; +import { AuditLogService } from './audit-log.service'; +import { RecentLogComponent } from './recent-log.component'; + +@NgModule({ + imports: [SharedModule], + declarations: [ + AuditLogComponent, + RecentLogComponent], + providers: [AuditLogService], + exports: [ + AuditLogComponent, + RecentLogComponent] +}) +export class LogModule { } \ No newline at end of file diff --git a/src/ui_ng/src/app/log/recent-log.component.css b/src/ui_ng/src/app/log/recent-log.component.css new file mode 100644 index 000000000..78a60e959 --- /dev/null +++ b/src/ui_ng/src/app/log/recent-log.component.css @@ -0,0 +1,42 @@ +.h2-log-override { + margin-top: 0px !important; +} + +.action-head-pos { + padding-right: 18px; +} + +.refresh-btn { + cursor: pointer; +} + +.refresh-btn:hover { + color: #00bfff; +} + +.custom-lines-button { + padding: 0px !important; + min-width: 25px !important; +} + +.lines-button-toggole { + font-size: 16px; + text-decoration: underline; +} + +.log-select { + width: 130px; + display: inline-block; + top: 1px; +} + +.item-divider { + height: 24px; + display: inline-block; + width: 1px; + background-color: #ccc; + opacity: 0.55; + margin-left: 12px; + top: 8px; + position: relative; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/log/recent-log.component.html b/src/ui_ng/src/app/log/recent-log.component.html new file mode 100644 index 000000000..34777cfde --- /dev/null +++ b/src/ui_ng/src/app/log/recent-log.component.html @@ -0,0 +1,38 @@ +
+

{{'SIDE_NAV.LOGS' | translate}}

+
+
+
+
+ +
+
+ + + + + +
+
+
+ + {{'AUDIT_LOG.USERNAME' | translate}} + {{'AUDIT_LOG.REPOSITORY_NAME' | translate}} + {{'AUDIT_LOG.TAGS' | translate}} + {{'AUDIT_LOG.OPERATION' | translate}} + {{'AUDIT_LOG.TIMESTAMP' | translate}} + + {{l.username}} + {{l.repo_name}} + {{l.repo_tag}} + {{l.operation}} + {{l.op_time | date: 'short'}} + + {{ (recentLogs ? recentLogs.length : 0) }} {{'AUDIT_LOG.ITEMS' | translate}} + +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/log/recent-log.component.ts b/src/ui_ng/src/app/log/recent-log.component.ts new file mode 100644 index 000000000..bf618e494 --- /dev/null +++ b/src/ui_ng/src/app/log/recent-log.component.ts @@ -0,0 +1,112 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { AuditLog } from './audit-log'; +import { SessionUser } from '../shared/session-user'; + +import { AuditLogService } from './audit-log.service'; +import { SessionService } from '../shared/session.service'; +import { MessageService } from '../global-message/message.service'; +import { AlertType } from '../shared/shared.const'; +import { errorHandler, accessErrorHandler } from '../shared/shared.utils'; + +@Component({ + selector: 'recent-log', + templateUrl: './recent-log.component.html', + styleUrls: ['recent-log.component.css'] +}) + +export class RecentLogComponent implements OnInit { + private sessionUser: SessionUser = null; + private recentLogs: AuditLog[]; + private logsCache: AuditLog[]; + private onGoing: boolean = false; + private lines: number = 10; //Support 10, 25 and 50 + currentTerm: string; + + constructor( + private session: SessionService, + private msgService: MessageService, + private logService: AuditLogService) { + this.sessionUser = this.session.getCurrentUser();//Initialize session + } + + ngOnInit(): void { + this.retrieveLogs(); + } + + private handleOnchange($event: any) { + this.currentTerm = ''; + if ($event && $event.target && $event.target["value"]) { + this.lines = $event.target["value"]; + if (this.lines < 10) { + this.lines = 10; + } + this.retrieveLogs(); + } + } + + public get logNumber(): number { + return this.recentLogs?this.recentLogs.length:0; + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public doFilter(terms: string): void { + if (terms.trim() === "") { + this.recentLogs = this.logsCache.filter(log => log.username != ""); + return; + } + this.currentTerm = terms; + this.recentLogs = this.logsCache.filter(log => this.isMatched(terms, log)); + } + + public refresh(): void { + this.retrieveLogs(); + } + + private retrieveLogs(): void { + if (this.lines < 10) { + this.lines = 10; + } + + this.onGoing = true; + this.logService.getRecentLogs(this.lines) + .subscribe( + response => { + this.onGoing = false; + this.logsCache = response; //Keep the data + this.recentLogs = this.logsCache.filter(log => log.username != "");//To display + }, + error => { + this.onGoing = false; + if (!accessErrorHandler(error, this.msgService)) { + this.msgService.announceMessage(error.status, errorHandler(error), AlertType.DANGER); + } + } + ); + } + + private isMatched(terms: string, log: AuditLog): boolean { + let reg = new RegExp('.*' + terms + '.*', 'i'); + return reg.test(log.username) || + reg.test(log.repo_name) || + reg.test(log.operation) || + reg.test(log.repo_tag); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/create-project/create-project.component.html b/src/ui_ng/src/app/project/create-project/create-project.component.html new file mode 100644 index 000000000..97f838bb2 --- /dev/null +++ b/src/ui_ng/src/app/project/create-project/create-project.component.html @@ -0,0 +1,39 @@ + + + + + + diff --git a/src/ui_ng/src/app/project/create-project/create-project.component.ts b/src/ui_ng/src/app/project/create-project/create-project.component.ts new file mode 100644 index 000000000..4174157c3 --- /dev/null +++ b/src/ui_ng/src/app/project/create-project/create-project.component.ts @@ -0,0 +1,129 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, EventEmitter, Output, ViewChild, AfterViewChecked, HostBinding } from '@angular/core'; +import { Response } from '@angular/http'; +import { NgForm } from '@angular/forms'; + +import { Project } from '../project'; +import { ProjectService } from '../project.service'; + +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; + +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'create-project', + templateUrl: 'create-project.component.html', + styleUrls: [ 'create-project.css' ] +}) +export class CreateProjectComponent implements AfterViewChecked { + + projectForm: NgForm; + + @ViewChild('projectForm') + currentForm: NgForm; + + project: Project = new Project(); + initVal: Project = new Project(); + + createProjectOpened: boolean; + + hasChanged: boolean; + + staticBackdrop: boolean = true; + closable: boolean = false; + + @Output() create = new EventEmitter(); + @ViewChild(InlineAlertComponent) + private inlineAlert: InlineAlertComponent; + + constructor(private projectService: ProjectService, + private translateService: TranslateService, + private messageHandlerService: MessageHandlerService) {} + + onSubmit() { + this.projectService + .createProject(this.project.name, this.project.public ? 1 : 0) + .subscribe( + status=>{ + this.create.emit(true); + this.messageHandlerService.showSuccess('PROJECT.CREATED_SUCCESS'); + this.createProjectOpened = false; + }, + error=>{ + let errorMessage: string; + if (error instanceof Response) { + switch(error.status) { + case 409: + this.translateService.get('PROJECT.NAME_ALREADY_EXISTS').subscribe(res=>errorMessage = res); + break; + case 400: + this.translateService.get('PROJECT.NAME_IS_ILLEGAL').subscribe(res=>errorMessage = res); + break; + default: + this.translateService.get('PROJECT.UNKNOWN_ERROR').subscribe(res=>errorMessage = res); + } + if(this.messageHandlerService.isAppLevel(error)) { + this.messageHandlerService.handleError(error); + this.createProjectOpened = false; + } else { + this.inlineAlert.showInlineError(errorMessage); + } + } + }); + } + + onCancel() { + if(this.hasChanged) { + this.inlineAlert.showInlineConfirmation({message: 'ALERT.FORM_CHANGE_CONFIRMATION'}); + } else { + this.createProjectOpened = false; + this.projectForm.reset(); + } + + } + + ngAfterViewChecked(): void { + this.projectForm = this.currentForm; + if(this.projectForm) { + this.projectForm.valueChanges.subscribe(data=>{ + for(let i in data) { + let origin = this.initVal[i]; + let current = data[i]; + if(current && current !== origin) { + this.hasChanged = true; + break; + } else { + this.hasChanged = false; + this.inlineAlert.close(); + } + } + }); + } + } + + newProject() { + this.project = new Project(); + this.hasChanged = false; + this.createProjectOpened = true; + } + + confirmCancel(event: boolean): void { + this.createProjectOpened = false; + this.inlineAlert.close(); + this.projectForm.reset(); + } +} + diff --git a/src/ui_ng/src/app/project/create-project/create-project.css b/src/ui_ng/src/app/project/create-project/create-project.css new file mode 100644 index 000000000..526c33101 --- /dev/null +++ b/src/ui_ng/src/app/project/create-project/create-project.css @@ -0,0 +1,13 @@ +.inline-help-public { + color: #CCCCCC; + font-size: 12px; + line-height: 1.5em; + letter-spacing: 0.01em; + margin-top: 0; + padding: 0 14px; +} + +.form-group-label-override { + font-size: 14px; + font-weight: 400; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/list-project/list-project.component.html b/src/ui_ng/src/app/project/list-project/list-project.component.html new file mode 100644 index 000000000..1dd2afe12 --- /dev/null +++ b/src/ui_ng/src/app/project/list-project/list-project.component.html @@ -0,0 +1,23 @@ + + {{'PROJECT.NAME' | translate}} + {{'PROJECT.PUBLIC_OR_PRIVATE' | translate}} + {{'PROJECT.ROLE' | translate}} + {{'PROJECT.REPO_COUNT'| translate}} + {{'PROJECT.CREATION_TIME' | translate}} + + + + + + + {{p.name}} + {{ (p.public === 1 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}} + {{roleInfo[p.current_user_role_id] | translate}} + {{p.repo_count}} + {{p.creation_time | date: 'short'}} + + + {{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}} + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/project/list-project/list-project.component.ts b/src/ui_ng/src/app/project/list-project/list-project.component.ts new file mode 100644 index 000000000..e5b6e9180 --- /dev/null +++ b/src/ui_ng/src/app/project/list-project/list-project.component.ts @@ -0,0 +1,89 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, EventEmitter, Output, Input, OnInit } from '@angular/core'; +import { Router, NavigationExtras } from '@angular/router'; +import { Project } from '../project'; +import { ProjectService } from '../project.service'; + +import { SessionService } from '../../shared/session.service'; +import { SearchTriggerService } from '../../base/global-search/search-trigger.service'; +import { ProjectTypes, RoleInfo } from '../../shared/shared.const'; + +import { State } from 'clarity-angular'; + +@Component({ + selector: 'list-project', + templateUrl: 'list-project.component.html' +}) +export class ListProjectComponent implements OnInit { + + @Input() projects: Project[]; + + + @Input() totalPage: number; + @Input() totalRecordCount: number; + pageOffset: number = 1; + + @Input() filteredType: string; + + @Output() paginate = new EventEmitter(); + + @Output() toggle = new EventEmitter(); + @Output() delete = new EventEmitter(); + + roleInfo = RoleInfo; + + constructor( + private session: SessionService, + private router: Router, + private searchTrigger: SearchTriggerService) { } + + ngOnInit(): void { + } + + get showRoleInfo(): boolean { + return this.filteredType === ProjectTypes[0]; + } + + public get isSystemAdmin(): boolean { + let account = this.session.getCurrentUser(); + return account != null && account.has_admin_role > 0; + } + + goToLink(proId: number): void { + this.searchTrigger.closeSearch(true); + + let linkUrl = ['harbor', 'projects', proId, 'repository']; + this.router.navigate(linkUrl); + } + + refresh(state: State) { + this.paginate.emit(state); + } + + newReplicationRule(p: Project) { + if(p) { + this.router.navigateByUrl(`/harbor/projects/${p.project_id}/replication?is_create=true`); + } + } + + toggleProject(p: Project) { + this.toggle.emit(p); + } + + deleteProject(p: Project) { + this.delete.emit(p); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/member/add-member/add-member.component.css b/src/ui_ng/src/app/project/member/add-member/add-member.component.css new file mode 100644 index 000000000..0bcac82e9 --- /dev/null +++ b/src/ui_ng/src/app/project/member/add-member/add-member.component.css @@ -0,0 +1,4 @@ +.form-group-label-override { + font-size: 14px; + font-weight: 400; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/member/add-member/add-member.component.html b/src/ui_ng/src/app/project/member/add-member/add-member.component.html new file mode 100644 index 000000000..9da8644b1 --- /dev/null +++ b/src/ui_ng/src/app/project/member/add-member/add-member.component.html @@ -0,0 +1,41 @@ + + + + + + diff --git a/src/ui_ng/src/app/project/member/add-member/add-member.component.ts b/src/ui_ng/src/app/project/member/add-member/add-member.component.ts new file mode 100644 index 000000000..522460d70 --- /dev/null +++ b/src/ui_ng/src/app/project/member/add-member/add-member.component.ts @@ -0,0 +1,139 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, EventEmitter, Output, ViewChild, AfterViewChecked } from '@angular/core'; +import { Response } from '@angular/http'; +import { NgForm } from '@angular/forms'; + +import { MemberService } from '../member.service'; + +import { MessageHandlerService } from '../../../shared/message-handler/message-handler.service'; +import { InlineAlertComponent } from '../../../shared/inline-alert/inline-alert.component'; + +import { TranslateService } from '@ngx-translate/core'; + +import { Member } from '../member'; + +@Component({ + selector: 'add-member', + templateUrl: 'add-member.component.html', + styleUrls: [ 'add-member.component.css' ] +}) +export class AddMemberComponent implements AfterViewChecked { + + member: Member = new Member(); + initVal: Member = new Member(); + + addMemberOpened: boolean; + + memberForm: NgForm; + + staticBackdrop: boolean = true; + closable: boolean = false; + + @ViewChild('memberForm') + currentForm: NgForm; + + hasChanged: boolean; + + @ViewChild(InlineAlertComponent) + inlineAlert: InlineAlertComponent; + + @Input() projectId: number; + @Output() added = new EventEmitter(); + + constructor(private memberService: MemberService, + private messageHandlerService: MessageHandlerService, + private translateService: TranslateService) {} + + onSubmit(): void { + if(!this.member.username || this.member.username.length === 0) { return; } + this.memberService + .addMember(this.projectId, this.member.username, +this.member.role_id) + .subscribe( + response=>{ + this.messageHandlerService.showSuccess('MEMBER.ADDED_SUCCESS'); + console.log('Added member successfully.'); + this.added.emit(true); + this.addMemberOpened = false; + }, + error=>{ + if (error instanceof Response) { + let errorMessageKey: string; + switch(error.status){ + case 404: + errorMessageKey = 'MEMBER.USERNAME_DOES_NOT_EXISTS'; + break; + case 409: + errorMessageKey = 'MEMBER.USERNAME_ALREADY_EXISTS'; + break; + default: + errorMessageKey = 'MEMBER.UNKNOWN_ERROR'; + } + if(this.messageHandlerService.isAppLevel(error)) { + this.messageHandlerService.handleError(error); + this.addMemberOpened = false; + } else { + this.translateService + .get(errorMessageKey) + .subscribe(errorMessage=>this.inlineAlert.showInlineError(errorMessage)); + } + } + console.log('Failed to add member of project:' + this.projectId, ' with error:' + error); + } + ); + } + + onCancel() { + if(this.hasChanged) { + this.inlineAlert.showInlineConfirmation({message: 'ALERT.FORM_CHANGE_CONFIRMATION'}); + } else { + this.addMemberOpened = false; + this.memberForm.reset(); + } + } + + ngAfterViewChecked(): void { + this.memberForm = this.currentForm; + if(this.memberForm) { + this.memberForm.valueChanges.subscribe(data=>{ + for(let i in data) { + let origin = this.initVal[i]; + let current = data[i]; + if(current && current !== origin) { + this.hasChanged = true; + break; + } else { + this.hasChanged = false; + this.inlineAlert.close(); + } + } + }); + } + } + + confirmCancel(confirmed: boolean) { + this.addMemberOpened = false; + this.inlineAlert.close(); + this.memberForm.reset(); + } + + openAddMemberModal(): void { + this.memberForm.reset(); + this.member = new Member(); + this.addMemberOpened = true; + this.hasChanged = false; + this.member.role_id = 1; + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/member/member.component.css b/src/ui_ng/src/app/project/member/member.component.css new file mode 100644 index 000000000..c782e3827 --- /dev/null +++ b/src/ui_ng/src/app/project/member/member.component.css @@ -0,0 +1,12 @@ +.option-left { + padding-left: 16px; + margin-top: 24px; +} +.option-right { + padding-right: 16px; + margin-top: 18px; +} + +.datagrid-foot { + margin-bottom: 0.5px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/member/member.component.html b/src/ui_ng/src/app/project/member/member.component.html new file mode 100644 index 000000000..19e87e1d6 --- /dev/null +++ b/src/ui_ng/src/app/project/member/member.component.html @@ -0,0 +1,33 @@ +
+
+
+
+ + +
+
+ + + + +
+
+
+
+ + {{'MEMBER.NAME' | translate}} + {{'MEMBER.ROLE' | translate}} + + + + + + + + {{m.username}} + {{roleInfo[m.role_id] | translate}} + + {{ (members ? members.length : 0) }} {{'MEMBER.ITEMS' | translate}} + +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/project/member/member.component.ts b/src/ui_ng/src/app/project/member/member.component.ts new file mode 100644 index 000000000..8f08d8879 --- /dev/null +++ b/src/ui_ng/src/app/project/member/member.component.ts @@ -0,0 +1,164 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; +import { ActivatedRoute, Params, Router } from '@angular/router'; +import { Response } from '@angular/http'; + +import { SessionUser } from '../../shared/session-user'; +import { Member } from './member'; +import { MemberService } from './member.service'; + +import { AddMemberComponent } from './add-member/add-member.component'; + +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; +import { ConfirmationTargets, ConfirmationState } from '../../shared/shared.const'; + +import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service'; +import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message'; +import { SessionService } from '../../shared/session.service'; + +import { RoleInfo } from '../../shared/shared.const'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/switchMap'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/observable/throw'; +import { Subscription } from 'rxjs/Subscription'; + +import { Project } from '../../project/project'; + +@Component({ + templateUrl: 'member.component.html', + styleUrls: ['./member.component.css'] +}) +export class MemberComponent implements OnInit, OnDestroy { + + members: Member[]; + projectId: number; + roleInfo = RoleInfo; + private delSub: Subscription; + + @ViewChild(AddMemberComponent) + addMemberComponent: AddMemberComponent; + + currentUser: SessionUser; + hasProjectAdminRole: boolean; + + searchMember: string; + + constructor( + private route: ActivatedRoute, + private router: Router, + private memberService: MemberService, + private messageHandlerService: MessageHandlerService, + private deletionDialogService: ConfirmationDialogService, + private session: SessionService) { + + this.delSub = deletionDialogService.confirmationConfirm$.subscribe(message => { + if (message && + message.state === ConfirmationState.CONFIRMED && + message.source === ConfirmationTargets.PROJECT_MEMBER) { + this.memberService + .deleteMember(this.projectId, message.data) + .subscribe( + response => { + this.messageHandlerService.showSuccess('MEMBER.DELETED_SUCCESS'); + console.log('Successful delete member: ' + message.data); + this.retrieve(this.projectId, ''); + }, + error => this.messageHandlerService.handleError(error) + ); + } + }); + } + + retrieve(projectId: number, username: string) { + this.memberService + .listMembers(projectId, username) + .subscribe( + response => this.members = response, + error => { + this.router.navigate(['/harbor', 'projects']); + this.messageHandlerService.handleError(error); + }); + } + + ngOnDestroy() { + if (this.delSub) { + this.delSub.unsubscribe(); + } + } + + ngOnInit() { + //Get projectId from route params snapshot. + this.projectId = +this.route.snapshot.parent.params['id']; + console.log('Get projectId from route params snapshot:' + this.projectId); + + this.currentUser = this.session.getCurrentUser(); + //Get current user from registered resolver. + let resolverData = this.route.snapshot.parent.data; + if(resolverData) { + this.hasProjectAdminRole = (resolverData['projectResolver']).has_project_admin_role; + } + + + + this.retrieve(this.projectId, ''); + } + + openAddMemberModal() { + this.addMemberComponent.openAddMemberModal(); + } + + addedMember() { + this.searchMember = ''; + this.retrieve(this.projectId, ''); + } + + changeRole(m: Member, roleId: number) { + if(m) { + this.memberService + .changeMemberRole(this.projectId, m.user_id, roleId) + .subscribe( + response => { + this.messageHandlerService.showSuccess('MEMBER.SWITCHED_SUCCESS'); + console.log('Successful change role with user ' + m.user_id + ' to roleId ' + roleId); + this.retrieve(this.projectId, ''); + }, + error => this.messageHandlerService.handleError(error) + ); + } + } + + deleteMember(m: Member) { + let deletionMessage: ConfirmationMessage = new ConfirmationMessage( + 'MEMBER.DELETION_TITLE', + 'MEMBER.DELETION_SUMMARY', + m.username, + m.user_id, + ConfirmationTargets.PROJECT_MEMBER + ); + this.deletionDialogService.openComfirmDialog(deletionMessage); + } + + doSearch(searchMember) { + this.searchMember = searchMember; + this.retrieve(this.projectId, this.searchMember); + } + + refresh() { + this.retrieve(this.projectId, ''); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/member/member.service.ts b/src/ui_ng/src/app/project/member/member.service.ts new file mode 100644 index 000000000..b050d1be5 --- /dev/null +++ b/src/ui_ng/src/app/project/member/member.service.ts @@ -0,0 +1,60 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/observable/throw'; + +import { Member } from './member'; + +@Injectable() +export class MemberService { + + constructor(private http: Http) {} + + listMembers(projectId: number, username: string): Observable { + console.log('Get member from project_id:' + projectId + ', username:' + username); + return this.http + .get(`/api/projects/${projectId}/members?username=${username}`) + .map(response=>response.json() as Member[]) + .catch(error=>Observable.throw(error)); + } + + addMember(projectId: number, username: string, roleId: number): Observable { + console.log('Adding member with username:' + username + ', roleId:' + roleId + ' under projectId:' + projectId); + return this.http + .post(`/api/projects/${projectId}/members`, { username: username, roles: [ roleId ] }) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + changeMemberRole(projectId: number, userId: number, roleId: number): Observable { + console.log('Changing member role with userId:' + ' to roleId:' + roleId + ' under projectId:' + projectId); + return this.http + .put(`/api/projects/${projectId}/members/${userId}`, { roles: [ roleId ]}) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + deleteMember(projectId: number, userId: number): Observable { + console.log('Deleting member role with userId:' + userId + ' under projectId:' + projectId); + return this.http + .delete(`/api/projects/${projectId}/members/${userId}`) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/member/member.ts b/src/ui_ng/src/app/project/member/member.ts new file mode 100644 index 000000000..0e3cd0107 --- /dev/null +++ b/src/ui_ng/src/app/project/member/member.ts @@ -0,0 +1,38 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* +{ + "user_id": 1, + "username": "admin", + "email": "", + "password": "", + "realname": "", + "comment": "", + "deleted": 0, + "role_name": "projectAdmin", + "role_id": 1, + "has_admin_role": 0, + "reset_uuid": "", + "creation_time": "0001-01-01T00:00:00Z", + "update_time": "0001-01-01T00:00:00Z" +} +*/ + +export class Member { + user_id: number; + username: string; + role_name: string; + has_admin_role: number; + role_id: number; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project-detail/project-detail.component.css b/src/ui_ng/src/app/project/project-detail/project-detail.component.css new file mode 100644 index 000000000..25f27c693 --- /dev/null +++ b/src/ui_ng/src/app/project/project-detail/project-detail.component.css @@ -0,0 +1,15 @@ +.sub-header-title { + margin-top: 12px; + margin-bottom: 12px; +} + +.sub-nav-bg-color { + background-color: #fafafa; +} + +.role-label { + color: #CCCCCC; + font-size: 14px; + font-style: italic; + letter-spacing: 0.01em; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project-detail/project-detail.component.html b/src/ui_ng/src/app/project/project-detail/project-detail.component.html new file mode 100644 index 000000000..ff651917b --- /dev/null +++ b/src/ui_ng/src/app/project/project-detail/project-detail.component.html @@ -0,0 +1,21 @@ +< {{'PROJECT_DETAIL.PROJECTS' | translate}} +< {{'SEARCH.BACK' | translate}} + +

{{currentProject.name}} {{roleName | translate}}

+ + \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project-detail/project-detail.component.ts b/src/ui_ng/src/app/project/project-detail/project-detail.component.ts new file mode 100644 index 000000000..c0d4a3a4b --- /dev/null +++ b/src/ui_ng/src/app/project/project-detail/project-detail.component.ts @@ -0,0 +1,60 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; + +import { Project } from '../project'; + +import { SessionService } from '../../shared/session.service'; +import { ProjectService } from '../../project/project.service'; + +import { RoleMapping } from '../../shared/shared.const'; + +@Component({ + selector: 'project-detail', + templateUrl: "project-detail.component.html", + styleUrls: [ 'project-detail.component.css' ] +}) +export class ProjectDetailComponent { + + hasSignedIn: boolean; + currentProject: Project; + + isMember: boolean; + roleName: string; + + constructor( + private route: ActivatedRoute, + private router: Router, + private sessionService: SessionService, + private projectService: ProjectService) { + + this.hasSignedIn = this.sessionService.getCurrentUser() !== null; + this.route.data.subscribe(data=>{ + this.currentProject = data['projectResolver']; + this.isMember = this.currentProject.is_member; + this.roleName = RoleMapping[this.currentProject.role_name]; + }); + } + + public get isSystemAdmin(): boolean { + let account = this.sessionService.getCurrentUser(); + return account != null && account.has_admin_role > 0; + } + + public get isSessionValid(): boolean { + return this.sessionService.getCurrentUser() != null; + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project-routing-resolver.service.ts b/src/ui_ng/src/app/project/project-routing-resolver.service.ts new file mode 100644 index 000000000..71951592e --- /dev/null +++ b/src/ui_ng/src/app/project/project-routing-resolver.service.ts @@ -0,0 +1,64 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'; + +import { Project } from './project'; +import { ProjectService } from './project.service'; +import { SessionService } from '../shared/session.service'; +import 'rxjs/add/operator/mergeMap'; + +@Injectable() +export class ProjectRoutingResolver implements Resolve{ + + constructor( + private sessionService: SessionService, + private projectService: ProjectService, + private router: Router) {} + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + let projectId = route.params['id']; + console.log('Project resolver, projectID:' + projectId); + return this.projectService + .getProject(projectId) + .toPromise() + .then((project: Project)=> { + if(project) { + let currentUser = this.sessionService.getCurrentUser(); + if(currentUser) { + let projectMembers = this.sessionService.getProjectMembers(); + if(projectMembers) { + let currentMember = projectMembers.find(m=>m.user_id === currentUser.user_id); + if(currentMember) { + project.is_member = true; + project.has_project_admin_role = (currentMember.role_name === 'projectAdmin'); + project.role_name = currentMember.role_name; + } + } + if(currentUser.has_admin_role === 1) { + project.has_project_admin_role = true; + } + } + return project; + } else { + this.router.navigate(['/harbor', 'projects']); + return null; + } + }).catch(error=>{ + this.router.navigate(['/harbor', 'projects']); + return null; + }); + + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project.component.css b/src/ui_ng/src/app/project/project.component.css new file mode 100644 index 000000000..98fe7dedd --- /dev/null +++ b/src/ui_ng/src/app/project/project.component.css @@ -0,0 +1,13 @@ +.header-title { + margin-top: 0; +} + +.option-left { + padding-left: 16px; + margin-top: 12px; +} + +.option-right { + padding-right: 16px; + margin-top: 18px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project.component.html b/src/ui_ng/src/app/project/project.component.html new file mode 100644 index 000000000..b30a6570d --- /dev/null +++ b/src/ui_ng/src/app/project/project.component.html @@ -0,0 +1,29 @@ +
+
+
+

{{'PROJECT.PROJECTS' | translate}}

+
+ +
+
+
+
+ + +
+
+
+ +
+ + + + +
+
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/project/project.component.ts b/src/ui_ng/src/app/project/project.component.ts new file mode 100644 index 000000000..f964ba702 --- /dev/null +++ b/src/ui_ng/src/app/project/project.component.ts @@ -0,0 +1,203 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; + +import { Router } from '@angular/router'; + +import { Project } from './project'; +import { ProjectService } from './project.service'; + +import { CreateProjectComponent } from './create-project/create-project.component'; + +import { ListProjectComponent } from './list-project/list-project.component'; + +import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; +import { Message } from '../global-message/message'; + +import { Response } from '@angular/http'; + +import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service'; +import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message'; +import { ConfirmationTargets, ConfirmationState } from '../shared/shared.const'; + +import { Subscription } from 'rxjs/Subscription'; + +import { State } from 'clarity-angular'; + +import { AppConfigService } from '../app-config.service'; +import { SessionService } from '../shared/session.service'; +import { ProjectTypes } from '../shared/shared.const'; +import { StatisticHandler } from '../shared/statictics/statistic-handler.service'; + +@Component({ + selector: 'project', + templateUrl: 'project.component.html', + styleUrls: ['./project.component.css'] +}) +export class ProjectComponent implements OnInit, OnDestroy { + + selected = []; + changedProjects: Project[]; + projectTypes = ProjectTypes; + + @ViewChild(CreateProjectComponent) + creationProject: CreateProjectComponent; + + @ViewChild(ListProjectComponent) + listProject: ListProjectComponent; + + currentFilteredType: number = 0; + + subscription: Subscription; + + projectName: string; + isPublic: number; + + page: number = 1; + pageSize: number = 15; + + totalPage: number; + totalRecordCount: number; + + constructor( + private projectService: ProjectService, + private messageHandlerService: MessageHandlerService, + private appConfigService: AppConfigService, + private sessionService: SessionService, + private deletionDialogService: ConfirmationDialogService, + private statisticHandler: StatisticHandler) { + this.subscription = deletionDialogService.confirmationConfirm$.subscribe(message => { + if (message && + message.state === ConfirmationState.CONFIRMED && + message.source === ConfirmationTargets.PROJECT) { + let projectId = message.data; + this.projectService + .deleteProject(projectId) + .subscribe( + response => { + this.messageHandlerService.showSuccess('PROJECT.DELETED_SUCCESS'); + this.retrieve(); + this.statisticHandler.refresh(); + }, + error =>{ + if(error && error.status === 412) { + this.messageHandlerService.showError('PROJECT.FAILED_TO_DELETE_PROJECT', ''); + } else { + this.messageHandlerService.handleError(error); + } + } + ); + } + }); + + } + + ngOnInit(): void { + this.projectName = ''; + this.isPublic = 0; + + } + + ngOnDestroy(): void { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + get projectCreationRestriction(): boolean { + let account = this.sessionService.getCurrentUser(); + if(account) { + switch(this.appConfigService.getConfig().project_creation_restriction) { + case 'adminonly': + return (account.has_admin_role === 1); + case 'everyone': + return true; + } + } + return false; + } + + retrieve(state?: State): void { + if (state) { + this.page = state.page.to + 1; + } + this.projectService + .listProjects(this.projectName, this.isPublic, this.page, this.pageSize) + .subscribe( + response => { + this.totalRecordCount = response.headers.get('x-total-count'); + this.totalPage = Math.ceil(this.totalRecordCount / this.pageSize); + this.changedProjects = response.json(); + }, + error => this.messageHandlerService.handleError(error) + ); + } + + openModal(): void { + this.creationProject.newProject(); + } + + createProject(created: boolean) { + if (created) { + this.projectName = ''; + this.retrieve(); + this.statisticHandler.refresh(); + } + } + + doSearchProjects(projectName: string): void { + this.projectName = projectName; + this.retrieve(); + } + + doFilterProjects($event: any): void { + if ($event && $event.target && $event.target["value"]) { + this.currentFilteredType = $event.target["value"]; + this.isPublic = this.currentFilteredType; + this.retrieve(); + } + } + + toggleProject(p: Project) { + if (p) { + p.public === 0 ? p.public = 1 : p.public = 0; + this.projectService + .toggleProjectPublic(p.project_id, p.public) + .subscribe( + response => { + this.messageHandlerService.showSuccess('PROJECT.TOGGLED_SUCCESS'); + this.statisticHandler.refresh(); + }, + error => this.messageHandlerService.handleError(error) + ); + } + } + + deleteProject(p: Project) { + let deletionMessage = new ConfirmationMessage( + 'PROJECT.DELETION_TITLE', + 'PROJECT.DELETION_SUMMARY', + p.name, + p.project_id, + ConfirmationTargets.PROJECT + ); + this.deletionDialogService.openComfirmDialog(deletionMessage); + } + + refresh(): void { + this.retrieve(); + this.statisticHandler.refresh(); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project.module.ts b/src/ui_ng/src/app/project/project.module.ts new file mode 100644 index 000000000..490583ab9 --- /dev/null +++ b/src/ui_ng/src/app/project/project.module.ts @@ -0,0 +1,58 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; + +import { RouterModule } from '@angular/router'; +import { SharedModule } from '../shared/shared.module'; +import { RepositoryModule } from '../repository/repository.module'; +import { ReplicationModule } from '../replication/replication.module'; +import { LogModule } from '../log/log.module'; + +import { ProjectComponent } from './project.component'; +import { CreateProjectComponent } from './create-project/create-project.component'; +import { ListProjectComponent } from './list-project/list-project.component'; + +import { ProjectDetailComponent } from './project-detail/project-detail.component'; +import { MemberComponent } from './member/member.component'; +import { AddMemberComponent } from './member/add-member/add-member.component'; + +import { ProjectService } from './project.service'; +import { MemberService } from './member/member.service'; +import { ProjectRoutingResolver } from './project-routing-resolver.service'; + +import { TargetExistsValidatorDirective } from '../shared/target-exists-directive'; + +@NgModule({ + imports: [ + SharedModule, + RepositoryModule, + ReplicationModule, + LogModule, + RouterModule + ], + declarations: [ + ProjectComponent, + CreateProjectComponent, + ListProjectComponent, + ProjectDetailComponent, + MemberComponent, + AddMemberComponent, + TargetExistsValidatorDirective + ], + exports: [ProjectComponent, ListProjectComponent], + providers: [ProjectRoutingResolver, ProjectService, MemberService] +}) +export class ProjectModule { + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project.service.ts b/src/ui_ng/src/app/project/project.service.ts new file mode 100644 index 000000000..26446f877 --- /dev/null +++ b/src/ui_ng/src/app/project/project.service.ts @@ -0,0 +1,90 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; + +import { Http, Headers, RequestOptions, Response, URLSearchParams } from '@angular/http'; +import { Project } from './project'; + +import { Message } from '../global-message/message'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/observable/throw'; + + + +@Injectable() +export class ProjectService { + + headers = new Headers({'Content-type': 'application/json'}); + options = new RequestOptions({'headers': this.headers}); + + constructor(private http: Http) {} + + getProject(projectId: number): Observable { + return this.http + .get(`/api/projects/${projectId}`) + .map(response=>response.json()) + .catch(error=>Observable.throw(error)); + } + + listProjects(name: string, isPublic: number, page?: number, pageSize?: number): Observable{ + let params = new URLSearchParams(); + params.set('page', page + ''); + params.set('page_size', pageSize + ''); + return this.http + .get(`/api/projects?project_name=${name}&is_public=${isPublic}`, {search: params}) + .map(response=>response) + .catch(error=>Observable.throw(error)); + } + + createProject(name: string, isPublic: number): Observable { + return this.http + .post(`/api/projects`, + JSON.stringify({'project_name': name, 'public': isPublic}) + , this.options) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + toggleProjectPublic(projectId: number, isPublic: number): Observable { + return this.http + .put(`/api/projects/${projectId}/publicity`, { 'public': isPublic }, this.options) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + deleteProject(projectId: number): Observable { + return this.http + .delete(`/api/projects/${projectId}`) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + checkProjectExists(projectName: string): Observable { + return this.http + .head(`/api/projects/?project_name=${projectName}`) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + checkProjectMember(projectId: number): Observable { + return this.http + .get(`/api/projects/${projectId}/members`) + .map(response=>response.json()) + .catch(error=>Observable.throw(error)); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/project/project.ts b/src/ui_ng/src/app/project/project.ts new file mode 100644 index 000000000..6b35d6034 --- /dev/null +++ b/src/ui_ng/src/app/project/project.ts @@ -0,0 +1,48 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + [ + { + "project_id": 1, + "owner_id": 1, + "name": "library", + "creation_time": "2017-02-10T07:57:56Z", + "creation_time_str": "", + "deleted": 0, + "owner_name": "", + "public": 1, + "Togglable": true, + "update_time": "2017-02-10T07:57:56Z", + "current_user_role_id": 1, + "repo_count": 0 + } + ] +*/ +export class Project { + project_id: number; + owner_id: number; + name: string; + creation_time: Date; + creation_time_str: string; + deleted: number; + owner_name: string; + public: number; + Togglable: boolean; + update_time: Date; + current_user_role_id: number; + repo_count: number; + has_project_admin_role: boolean; + is_member: boolean; + role_name: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.css b/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.css new file mode 100644 index 000000000..0bcac82e9 --- /dev/null +++ b/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.css @@ -0,0 +1,4 @@ +.form-group-label-override { + font-size: 14px; + font-weight: 400; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.html b/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.html new file mode 100644 index 000000000..1d9572c7d --- /dev/null +++ b/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.html @@ -0,0 +1,53 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.ts b/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.ts new file mode 100644 index 000000000..57b94194d --- /dev/null +++ b/src/ui_ng/src/app/replication/create-edit-destination/create-edit-destination.component.ts @@ -0,0 +1,281 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Output, EventEmitter, ViewChild, AfterViewChecked } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { ReplicationService } from '../replication.service'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; +import { ActionType } from '../../shared/shared.const'; + +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; + +import { Target } from '../target'; + +import { TranslateService } from '@ngx-translate/core'; + +const FAKE_PASSWORD = 'rjGcfuRu'; + +@Component({ + selector: 'create-edit-destination', + templateUrl: './create-edit-destination.component.html', + styleUrls: [ 'create-edit-destination.component.css' ] +}) +export class CreateEditDestinationComponent implements AfterViewChecked { + + modalTitle: string; + createEditDestinationOpened: boolean; + + editable: boolean; + + testOngoing: boolean; + pingTestMessage: string; + pingStatus: boolean; + + actionType: ActionType; + + target: Target = new Target(); + initVal: Target = new Target(); + + targetForm: NgForm; + + staticBackdrop: boolean = true; + closable: boolean = false; + + @ViewChild('targetForm') + currentForm: NgForm; + + hasChanged: boolean; + + endpointHasChanged: boolean; + targetNameHasChanged: boolean; + + @ViewChild(InlineAlertComponent) + inlineAlert: InlineAlertComponent; + + @Output() reload = new EventEmitter(); + + constructor( + private replicationService: ReplicationService, + private messageHandlerService: MessageHandlerService, + private translateService: TranslateService) {} + + openCreateEditTarget(editable: boolean, targetId?: number) { + + this.target = new Target(); + this.createEditDestinationOpened = true; + this.editable = editable; + + this.hasChanged = false; + this.endpointHasChanged = false; + this.targetNameHasChanged = false; + + this.pingTestMessage = ''; + this.pingStatus = true; + this.testOngoing = false; + + if(targetId) { + this.actionType = ActionType.EDIT; + this.translateService.get('DESTINATION.TITLE_EDIT').subscribe(res=>this.modalTitle=res); + this.replicationService + .getTarget(targetId) + .subscribe( + target=>{ + this.target = target; + this.initVal.name = this.target.name; + this.initVal.endpoint = this.target.endpoint; + this.initVal.username = this.target.username; + this.initVal.password = FAKE_PASSWORD; + this.target.password = this.initVal.password; + }, + error=>this.messageHandlerService.handleError(error) + ); + } else { + this.actionType = ActionType.ADD_NEW; + this.translateService.get('DESTINATION.TITLE_ADD').subscribe(res=>this.modalTitle=res); + } + } + + testConnection() { + this.translateService.get('DESTINATION.TESTING_CONNECTION').subscribe(res=>this.pingTestMessage=res); + this.pingStatus = true; + this.testOngoing = !this.testOngoing; + + let payload: Target = new Target(); + if(this.endpointHasChanged) { + payload.endpoint = this.target.endpoint; + payload.username = this.target.username; + payload.password = this.target.password; + } else { + payload.id = this.target.id; + } + + this.replicationService + .pingTarget(payload) + .subscribe( + response=>{ + this.pingStatus = true; + this.translateService.get('DESTINATION.TEST_CONNECTION_SUCCESS').subscribe(res=>this.pingTestMessage=res); + this.testOngoing = !this.testOngoing; + }, + error=>{ + this.pingStatus = false; + this.translateService.get('DESTINATION.TEST_CONNECTION_FAILURE').subscribe(res=>this.pingTestMessage=res); + this.testOngoing = !this.testOngoing; + } + ) + } + + changedTargetName($event: any) { + if(this.editable) { + this.targetNameHasChanged = true; + } + } + + clearPassword($event: any) { + if(this.editable) { + this.target.password = ''; + this.endpointHasChanged = true; + } + } + + onSubmit() { + switch(this.actionType) { + case ActionType.ADD_NEW: + this.replicationService + .createTarget(this.target) + .subscribe( + response=>{ + this.messageHandlerService.showSuccess('DESTINATION.CREATED_SUCCESS'); + console.log('Successful added target.'); + this.createEditDestinationOpened = false; + this.reload.emit(true); + }, + error=>{ + let errorMessageKey = ''; + switch(error.status) { + case 409: + errorMessageKey = 'DESTINATION.CONFLICT_NAME'; + break; + case 400: + errorMessageKey = 'DESTINATION.INVALID_NAME'; + break; + default: + errorMessageKey = 'UNKNOWN_ERROR'; + } + + this.translateService + .get(errorMessageKey) + .subscribe(res=>{ + if(this.messageHandlerService.isAppLevel(error)) { + this.messageHandlerService.handleError(error); + this.createEditDestinationOpened = false; + } else { + this.inlineAlert.showInlineError(res); + } + }); + } + ); + break; + case ActionType.EDIT: + if(!(this.targetNameHasChanged || this.endpointHasChanged)) { + this.createEditDestinationOpened = false; + return; + } + let payload: Target = new Target(); + if(this.targetNameHasChanged) { + payload.name = this.target.name; + } + if (this.endpointHasChanged) { + payload.endpoint = this.target.endpoint; + payload.username = this.target.username; + payload.password = this.target.password; + } + this.replicationService + .updateTarget(payload, this.target.id) + .subscribe( + response=>{ + this.messageHandlerService.showSuccess('DESTINATION.UPDATED_SUCCESS'); + console.log('Successful updated target.'); + this.createEditDestinationOpened = false; + this.reload.emit(true); + }, + error=>{ + let errorMessageKey = ''; + switch(error.status) { + case 409: + errorMessageKey = 'DESTINATION.CONFLICT_NAME'; + break; + case 400: + errorMessageKey = 'DESTINATION.INVALID_NAME'; + break; + default: + errorMessageKey = 'UNKNOWN_ERROR'; + } + this.translateService + .get(errorMessageKey) + .subscribe(res=>{ + if(this.messageHandlerService.isAppLevel(error)) { + this.messageHandlerService.handleError(error); + this.createEditDestinationOpened = false; + } else { + this.inlineAlert.showInlineError(res); + } + }); + } + ); + break; + } + } + + onCancel() { + if(this.hasChanged) { + this.inlineAlert.showInlineConfirmation({message: 'ALERT.FORM_CHANGE_CONFIRMATION'}); + } else { + this.createEditDestinationOpened = false; + this.targetForm.reset(); + } + } + + confirmCancel(confirmed: boolean) { + this.createEditDestinationOpened = false; + this.inlineAlert.close(); + } + + mappedName: {} = { + 'targetName': 'name', + 'endpointUrl': 'endpoint', + 'username': 'username', + 'password': 'password' + }; + + ngAfterViewChecked(): void { + this.targetForm = this.currentForm; + if(this.targetForm) { + this.targetForm.valueChanges.subscribe(data=>{ + for(let i in data) { + let current = data[i]; + let origin = this.initVal[this.mappedName[i]]; + if(((this.actionType === ActionType.EDIT && this.editable && !current) || current) && current !== origin) { + this.hasChanged = true; + break; + } else { + this.hasChanged = false; + this.inlineAlert.close(); + } + } + }); + } + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/destination/destination.component.css b/src/ui_ng/src/app/replication/destination/destination.component.css new file mode 100644 index 000000000..c9617ab29 --- /dev/null +++ b/src/ui_ng/src/app/replication/destination/destination.component.css @@ -0,0 +1,8 @@ +.option-left { + padding-left: 16px; + margin-top: 24px; +} +.option-right { + padding-right: 16px; + margin-top: 36px; +} diff --git a/src/ui_ng/src/app/replication/destination/destination.component.html b/src/ui_ng/src/app/replication/destination/destination.component.html new file mode 100644 index 000000000..2115ff6c2 --- /dev/null +++ b/src/ui_ng/src/app/replication/destination/destination.component.html @@ -0,0 +1,33 @@ +
+
+
+
+ + +
+
+ + + + +
+
+
+
+ + {{'DESTINATION.NAME' | translate}} + {{'DESTINATION.URL' | translate}} + {{'DESTINATION.CREATION_TIME' | translate}} + + + + + + {{t.name}} + {{t.endpoint}} + {{t.creation_time | date: 'short'}} + + {{ (targets ? targets.length : 0) }} {{'DESTINATION.ITEMS' | translate}} + +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/replication/destination/destination.component.ts b/src/ui_ng/src/app/replication/destination/destination.component.ts new file mode 100644 index 000000000..e5c1bb7a7 --- /dev/null +++ b/src/ui_ng/src/app/replication/destination/destination.component.ts @@ -0,0 +1,146 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; +import { Target } from '../target'; +import { ReplicationService } from '../replication.service'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service'; +import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message'; + +import { ConfirmationTargets, ConfirmationState } from '../../shared/shared.const'; + +import { Subscription } from 'rxjs/Subscription'; + +import { CreateEditDestinationComponent } from '../create-edit-destination/create-edit-destination.component'; + +@Component({ + selector: 'destination', + templateUrl: 'destination.component.html', + styleUrls: ['./destination.component.css'] +}) +export class DestinationComponent implements OnInit { + + @ViewChild(CreateEditDestinationComponent) + createEditDestinationComponent: CreateEditDestinationComponent; + + targets: Target[]; + target: Target; + + targetName: string; + subscription: Subscription; + + constructor( + private replicationService: ReplicationService, + private messageHandlerService: MessageHandlerService, + private deletionDialogService: ConfirmationDialogService) { + this.subscription = this.deletionDialogService.confirmationConfirm$.subscribe(message => { + if (message && + message.source === ConfirmationTargets.TARGET && + message.state === ConfirmationState.CONFIRMED) { + let targetId = message.data; + this.replicationService + .deleteTarget(targetId) + .subscribe( + response => { + this.messageHandlerService.showSuccess('DESTINATION.DELETED_SUCCESS'); + this.reload(); + }, + error => { + if(error && error.status === 412) { + this.messageHandlerService.showError('DESTINATION.FAILED_TO_DELETE_TARGET_IN_USED', ''); + } else { + this.messageHandlerService.handleError(error); + } + }); + } + }); + } + + ngOnInit(): void { + this.targetName = ''; + this.retrieve(''); + } + + ngOnDestroy(): void { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + retrieve(targetName: string): void { + this.replicationService + .listTargets(targetName) + .subscribe( + targets => this.targets = targets, + error => this.messageHandlerService.handleError(error) + ); + } + + doSearchTargets(targetName: string) { + this.targetName = targetName; + this.retrieve(targetName); + } + + refreshTargets() { + this.retrieve(''); + } + + reload() { + this.targetName = ''; + this.retrieve(''); + } + + openModal() { + this.createEditDestinationComponent.openCreateEditTarget(true); + this.target = new Target(); + } + + editTarget(target: Target) { + if (target) { + let editable = true; + this.replicationService + .listTargetPolicies(target.id) + .subscribe( + policies=>{ + if(policies && policies.length > 0) { + for(let i = 0; i < policies.length; i++){ + let p = policies[i]; + if(p.enabled === 1) { + editable = false; + break; + } + } + } + this.createEditDestinationComponent.openCreateEditTarget(editable, target.id); + }, + error=>this.messageHandlerService.handleError(error) + ); + + } + } + + deleteTarget(target: Target) { + if (target) { + let targetId = target.id; + let deletionMessage = new ConfirmationMessage( + 'REPLICATION.DELETION_TITLE_TARGET', + 'REPLICATION.DELETION_SUMMARY_TARGET', + target.name, + target.id, + ConfirmationTargets.TARGET); + this.deletionDialogService.openComfirmDialog(deletionMessage); + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/job.ts b/src/ui_ng/src/app/replication/job.ts new file mode 100644 index 000000000..cbd84199f --- /dev/null +++ b/src/ui_ng/src/app/replication/job.ts @@ -0,0 +1,36 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + { + "id": 1, + "status": "running", + "repository": "library/mysql", + "policy_id": 1, + "operation": "transfer", + "tags": null, + "creation_time": "2017-02-24T06:44:04Z", + "update_time": "2017-02-24T06:44:04Z" + } + +*/ +export class Job { + id: number; + status: string; + repository: string; + policy_id: number; + operation: string; + tags: string; + creation_time: Date; + update_time: Date; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/list-job/list-job.component.html b/src/ui_ng/src/app/replication/list-job/list-job.component.html new file mode 100644 index 000000000..04c3ae24f --- /dev/null +++ b/src/ui_ng/src/app/replication/list-job/list-job.component.html @@ -0,0 +1,24 @@ + + {{'REPLICATION.NAME' | translate}} + {{'REPLICATION.STATUS' | translate}} + {{'REPLICATION.OPERATION' | translate}} + {{'REPLICATION.CREATION_TIME' | translate}} + {{'REPLICATION.END_TIME' | translate}} + {{'REPLICATION.LOGS' | translate}} + + {{j.repository}} + {{j.status}} + {{j.operation}} + {{j.creation_time | date: 'short'}} + {{j.update_time | date: 'short'}} + + + + + + + + {{ totalRecordCount }} {{'REPLICATION.ITEMS' | translate}} + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/list-job/list-job.component.ts b/src/ui_ng/src/app/replication/list-job/list-job.component.ts new file mode 100644 index 000000000..2d9a0ae11 --- /dev/null +++ b/src/ui_ng/src/app/replication/list-job/list-job.component.ts @@ -0,0 +1,38 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Job } from '../job'; +import { State } from 'clarity-angular'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +@Component({ + selector: 'list-job', + templateUrl: 'list-job.component.html' +}) +export class ListJobComponent { + @Input() jobs: Job[]; + @Input() totalRecordCount: number; + @Input() totalPage: number; + @Output() paginate = new EventEmitter(); + + constructor(private messageHandlerService: MessageHandlerService) {} + + pageOffset: number = 1; + + refresh(state: State) { + if(this.jobs) { + this.paginate.emit(state); + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/policy.ts b/src/ui_ng/src/app/replication/policy.ts new file mode 100644 index 000000000..f0aebd5af --- /dev/null +++ b/src/ui_ng/src/app/replication/policy.ts @@ -0,0 +1,48 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + { + "id": 1, + "project_id": 1, + "project_name": "library", + "target_id": 1, + "target_name": "target_01", + "name": "sync_01", + "enabled": 0, + "description": "sync_01 desc.", + "cron_str": "", + "start_time": "0001-01-01T00:00:00Z", + "creation_time": "2017-02-24T06:41:52Z", + "update_time": "2017-02-24T06:41:52Z", + "error_job_count": 0, + "deleted": 0 + } +*/ + +export class Policy { + id: number; + project_id: number; + project_name: string; + target_id: number; + target_name: string; + name: string; + enabled: number; + description: string; + cron_str: string; + start_time: Date; + creation_time: Date; + update_time: Date; + error_job_count: number; + deleted: number; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication-management/replication-management.component.css b/src/ui_ng/src/app/replication/replication-management/replication-management.component.css new file mode 100644 index 000000000..0286e4e83 --- /dev/null +++ b/src/ui_ng/src/app/replication/replication-management/replication-management.component.css @@ -0,0 +1,8 @@ +.sub-header-title { + margin-top: 0; + margin-bottom: 12px; +} + +.sub-nav-bg-color { + background-color: #fafafa; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication-management/replication-management.component.html b/src/ui_ng/src/app/replication/replication-management/replication-management.component.html new file mode 100644 index 000000000..3fd84365c --- /dev/null +++ b/src/ui_ng/src/app/replication/replication-management/replication-management.component.html @@ -0,0 +1,12 @@ +

{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}

+ + diff --git a/src/ui_ng/src/app/replication/replication-management/replication-management.component.ts b/src/ui_ng/src/app/replication/replication-management/replication-management.component.ts new file mode 100644 index 000000000..a81236c8b --- /dev/null +++ b/src/ui_ng/src/app/replication/replication-management/replication-management.component.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component } from '@angular/core'; + +@Component({ + selector: 'replication-management', + templateUrl: 'replication-management.component.html', + styleUrls: [ './replication-management.component.css' ] +}) +export class ReplicationManagementComponent {} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication.component.css b/src/ui_ng/src/app/replication/replication.component.css new file mode 100644 index 000000000..6adb08c62 --- /dev/null +++ b/src/ui_ng/src/app/replication/replication.component.css @@ -0,0 +1,17 @@ +.option-left { + padding-left: 16px; + margin-top: 24px; +} +.option-right { + padding-right: 16px; + margin-top: 18px; +} + +.option-left-down { + margin-top: 36px; +} + +.option-right-down { + padding-right: 16px; + margin-top: 24px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication.component.html b/src/ui_ng/src/app/replication/replication.component.html new file mode 100644 index 000000000..ac6a8e8f8 --- /dev/null +++ b/src/ui_ng/src/app/replication/replication.component.html @@ -0,0 +1,58 @@ +
+
+
+
+ + +
+
+ + + + + + + + +
+
+
+
+ +
+
+
+
{{'REPLICATION.REPLICATION_JOBS' | translate}}
+
+ + + + + +
+
+
+ + + + +
+ + +
+
+
+
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication.component.ts b/src/ui_ng/src/app/replication/replication.component.ts new file mode 100644 index 000000000..f111b8aff --- /dev/null +++ b/src/ui_ng/src/app/replication/replication.component.ts @@ -0,0 +1,249 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { CreateEditPolicyComponent } from '../shared/create-edit-policy/create-edit-policy.component'; + +import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; + +import { SessionService } from '../shared/session.service'; + +import { ReplicationService } from './replication.service'; + +import { SessionUser } from '../shared/session-user'; +import { Policy } from './policy'; +import { Job } from './job'; +import { Target } from './target'; + +import { State } from 'clarity-angular'; + +const ruleStatus = [ + { 'key': '', 'description': 'REPLICATION.ALL_STATUS'}, + { 'key': '1', 'description': 'REPLICATION.ENABLED'}, + { 'key': '0', 'description': 'REPLICATION.DISABLED'} +]; + +const jobStatus = [ + { 'key': '', 'description': 'REPLICATION.ALL' }, + { 'key': 'pending', 'description': 'REPLICATION.PENDING' }, + { 'key': 'running', 'description': 'REPLICATION.RUNNING' }, + { 'key': 'error', 'description': 'REPLICATION.ERROR' }, + { 'key': 'retrying', 'description': 'REPLICATION.RETRYING' }, + { 'key': 'stopped' , 'description': 'REPLICATION.STOPPED' }, + { 'key': 'finished', 'description': 'REPLICATION.FINISHED' }, + { 'key': 'canceled', 'description': 'REPLICATION.CANCELED' } +]; + +const optionalSearch: {} = {0: 'REPLICATION.ADVANCED', 1: 'REPLICATION.SIMPLE'}; + +class SearchOption { + policyId: number; + policyName: string = ''; + repoName: string = ''; + status: string = ''; + startTime: string = ''; + endTime: string = ''; + page: number = 1; + pageSize: number = 5; +} + +@Component({ + selector: 'replicaton', + templateUrl: 'replication.component.html', + styleUrls: ['./replication.component.css'] +}) +export class ReplicationComponent implements OnInit { + + currentUser: SessionUser; + projectId: number; + + search: SearchOption; + + ruleStatus = ruleStatus; + currentRuleStatus: {key: string, description: string}; + + jobStatus = jobStatus; + currentJobStatus: {key: string, description: string}; + + changedPolicies: Policy[]; + changedJobs: Job[]; + initSelectedId: number; + + policies: Policy[]; + jobs: Job[]; + + jobsTotalRecordCount: number; + jobsTotalPage: number; + + toggleJobSearchOption = optionalSearch; + currentJobSearchOption: number; + + @ViewChild(CreateEditPolicyComponent) + createEditPolicyComponent: CreateEditPolicyComponent; + + constructor( + private sessionService: SessionService, + private messageHandlerService: MessageHandlerService, + private replicationService: ReplicationService, + private route: ActivatedRoute) { + this.currentUser = this.sessionService.getCurrentUser(); + } + + ngOnInit(): void { + this.projectId = +this.route.snapshot.parent.params['id']; + this.search = new SearchOption(); + this.currentRuleStatus = this.ruleStatus[0]; + this.currentJobStatus = this.jobStatus[0]; + this.currentJobSearchOption = 0; + this.retrievePolicies(); + + let isCreate = this.route.snapshot.parent.queryParams['is_create']; + if (isCreate && isCreate) { + this.openModal(); + } + } + + retrievePolicies(): void { + this.replicationService + .listPolicies(this.search.policyName, this.projectId) + .subscribe( + response=>{ + this.changedPolicies = response; + if(this.changedPolicies && this.changedPolicies.length > 0) { + this.initSelectedId = this.changedPolicies[0].id; + } + this.policies = this.changedPolicies; + if(this.changedPolicies && this.changedPolicies.length > 0) { + this.search.policyId = this.changedPolicies[0].id; + this.fetchPolicyJobs(); + } else { + this.changedJobs = []; + } + }, + error=>this.messageHandlerService.handleError(error) + ); + } + + openModal(): void { + this.createEditPolicyComponent.openCreateEditPolicy(true); + } + + openEditPolicy(policy: Policy) { + if(policy) { + let editable = true; + if(policy.enabled === 1) { + editable = false; + } + this.createEditPolicyComponent.openCreateEditPolicy(editable, policy.id); + } + } + + fetchPolicyJobs(state?: State) { + if(state) { + this.search.page = state.page.to + 1; + } + this.replicationService + .listJobs(this.search.policyId, this.search.status, this.search.repoName, + this.search.startTime, this.search.endTime, this.search.page, this.search.pageSize) + .subscribe( + response=>{ + this.jobsTotalRecordCount = response.headers.get('x-total-count'); + this.jobsTotalPage = Math.ceil(this.jobsTotalRecordCount / this.search.pageSize); + this.changedJobs = response.json(); + this.jobs = this.changedJobs; + for(let i = 0; i < this.jobs.length; i++) { + let j = this.jobs[i]; + if(j.status == 'retrying' || j.status == 'error') { + this.messageHandlerService.showError('REPLICATION.FOUND_ERROR_IN_JOBS', ''); + break; + } + } + }, + error=>this.messageHandlerService.handleError(error) + ); + } + + selectOnePolicy(policy: Policy) { + if(policy) { + this.search.policyId = policy.id; + this.search.repoName = ''; + this.search.status = '' + this.currentJobSearchOption = 0; + this.currentJobStatus = { 'key': '', 'description': 'REPLICATION.ALL'}; + this.fetchPolicyJobs(); + } + } + + doSearchPolicies(policyName: string) { + this.search.policyName = policyName; + this.retrievePolicies(); + } + + doFilterPolicyStatus(status: string) { + this.currentRuleStatus = this.ruleStatus.find(r=>r.key === status); + if(status.trim() === '') { + this.changedPolicies = this.policies; + } else { + this.changedPolicies = this.policies.filter(policy=>policy.enabled === +this.currentRuleStatus.key); + } + } + + doFilterJobStatus(status: string) { + this.currentJobStatus = this.jobStatus.find(r=>r.key === status); + this.search.status = status; + this.doSearchJobs(this.search.repoName); + } + + doSearchJobs(repoName: string) { + this.search.repoName = repoName; + this.fetchPolicyJobs(); + } + + reloadPolicies(isReady: boolean) { + if(isReady) { + this.search.policyName = ''; + this.retrievePolicies(); + } + } + + refreshPolicies() { + this.retrievePolicies(); + } + + refreshJobs() { + this.fetchPolicyJobs(); + } + + toggleSearchJobOptionalName(option: number) { + (option === 1) ? this.currentJobSearchOption = 0 : this.currentJobSearchOption = 1; + } + + doJobSearchByStartTime(strDate: string) { + if(!strDate || strDate.trim() === '') { + strDate = 0 + ''; + } + (strDate === '0') ? this.search.startTime = '' : this.search.startTime = (new Date(strDate).getTime() / 1000) + ''; + this.fetchPolicyJobs(); + } + + doJobSearchByEndTime(strDate: string) { + if(!strDate || strDate.trim() === '') { + strDate = 0 + ''; + } + let oneDayOffset = 3600 * 24; + (strDate === '0') ? this.search.endTime = '' : this.search.endTime = (new Date(strDate).getTime() / 1000 + oneDayOffset) + ''; + this.fetchPolicyJobs(); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication.module.ts b/src/ui_ng/src/app/replication/replication.module.ts new file mode 100644 index 000000000..46f44253c --- /dev/null +++ b/src/ui_ng/src/app/replication/replication.module.ts @@ -0,0 +1,43 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { ReplicationManagementComponent } from './replication-management/replication-management.component'; + +import { ReplicationComponent } from './replication.component'; +import { ListJobComponent } from './list-job/list-job.component'; +import { TotalReplicationComponent } from './total-replication/total-replication.component'; +import { DestinationComponent } from './destination/destination.component'; +import { CreateEditDestinationComponent } from './create-edit-destination/create-edit-destination.component'; + +import { SharedModule } from '../shared/shared.module'; +import { ReplicationService } from './replication.service'; + +@NgModule({ + imports: [ + SharedModule, + RouterModule + ], + declarations: [ + ReplicationComponent, + ReplicationManagementComponent, + ListJobComponent, + TotalReplicationComponent, + DestinationComponent, + CreateEditDestinationComponent + ], + exports: [ ReplicationComponent ], + providers: [ ReplicationService ] +}) +export class ReplicationModule {} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/replication.service.ts b/src/ui_ng/src/app/replication/replication.service.ts new file mode 100644 index 000000000..89a49e02e --- /dev/null +++ b/src/ui_ng/src/app/replication/replication.service.ts @@ -0,0 +1,181 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Http, URLSearchParams, Response } from '@angular/http'; + +import { Policy } from './policy'; +import { Job } from './job'; +import { Target } from './target'; + +import { Observable } from 'rxjs/Observable'; +import 'rxjs/add/operator/catch'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/observable/throw'; +import 'rxjs/add/operator/mergeMap'; + +@Injectable() +export class ReplicationService { + constructor(private http: Http) {} + + listPolicies(policyName: string, projectId?: any): Observable { + if(!projectId) { + projectId = ''; + } + return this.http + .get(`/api/policies/replication?project_id=${projectId}&name=${policyName}`) + .map(response=>response.json() as Policy[]) + .catch(error=>Observable.throw(error)); + } + + getPolicy(policyId: number): Observable { + return this.http + .get(`/api/policies/replication/${policyId}`) + .map(response=>response.json() as Policy) + .catch(error=>Observable.throw(error)); + } + + createPolicy(policy: Policy): Observable { + return this.http + .post(`/api/policies/replication`, JSON.stringify(policy)) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + updatePolicy(policy: Policy): Observable { + if (policy && policy.id) { + return this.http + .put(`/api/policies/replication/${policy.id}`, JSON.stringify(policy)) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + return Observable.throw(new Error("Policy is nil or has no ID set.")); + } + + createOrUpdatePolicyWithNewTarget(policy: Policy, target: Target): Observable { + return this.http + .post(`/api/targets`, JSON.stringify(target)) + .map(response=>{ + return response.status; + }) + .catch(error=>Observable.throw(error)) + .flatMap((status)=>{ + if(status === 201) { + return this.http + .get(`/api/targets?name=${target.name}`) + .map(res=>res) + .catch(error=>Observable.throw(error)); + } + }) + .flatMap((res: Response) => { + if(res.status === 200) { + let lastAddedTarget= res.json()[0]; + if(lastAddedTarget && lastAddedTarget.id) { + policy.target_id = lastAddedTarget.id; + if(policy.id) { + return this.http + .put(`/api/policies/replication/${policy.id}`, JSON.stringify(policy)) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } else { + return this.http + .post(`/api/policies/replication`, JSON.stringify(policy)) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + } + } + }) + .catch(error=>Observable.throw(error)); + } + + enablePolicy(policyId: number, enabled: number): Observable { + console.log('Enable or disable policy ID:' + policyId + ' with activation status:' + enabled); + return this.http + .put(`/api/policies/replication/${policyId}/enablement`, {enabled: enabled}) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + deletePolicy(policyId: number): Observable { + console.log('Delete policy ID:' + policyId); + return this.http + .delete(`/api/policies/replication/${policyId}`) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + // /api/jobs/replication/?page=1&page_size=20&end_time=&policy_id=1&start_time=&status=&repository= + listJobs(policyId: number, status: string = '', repoName: string = '', startTime: string = '', endTime: string = '', page: number, pageSize: number): Observable { + return this.http + .get(`/api/jobs/replication?policy_id=${policyId}&status=${status}&repository=${repoName}&start_time=${startTime}&end_time=${endTime}&page=${page}&page_size=${pageSize}`) + .map(response=>response) + .catch(error=>Observable.throw(error)); + } + + listTargets(targetName: string): Observable { + return this.http + .get(`/api/targets?name=${targetName}`) + .map(response=>response.json() as Target[]) + .catch(error=>Observable.throw(error)); + } + + listTargetPolicies(targetId: number): Observable { + return this.http + .get(`/api/targets/${targetId}/policies`) + .map(response=>response.json() as Policy[]) + .catch(error=>Observable.throw(error)); + } + + getTarget(targetId: number): Observable { + return this.http + .get(`/api/targets/${targetId}`) + .map(response=>response.json() as Target) + .catch(error=>Observable.throw(error)); + } + + createTarget(target: Target): Observable { + return this.http + .post(`/api/targets`, JSON.stringify(target)) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + pingTarget(target: Target): Observable { + if(target.id) { + return this.http + .post(`/api/targets/${target.id}/ping`, {}) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + return this.http + .post(`/api/targets/ping`, target) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + updateTarget(target: Target, targetId: number): Observable { + return this.http + .put(`/api/targets/${targetId}`, JSON.stringify(target)) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + deleteTarget(targetId: number): Observable { + return this.http + .delete(`/api/targets/${targetId}`) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/target.ts b/src/ui_ng/src/app/replication/target.ts new file mode 100644 index 000000000..47e5bff8d --- /dev/null +++ b/src/ui_ng/src/app/replication/target.ts @@ -0,0 +1,36 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + { + "id": 1, + "endpoint": "http://10.117.4.151", + "name": "target_01", + "username": "admin", + "password": "Harbor12345", + "type": 0, + "creation_time": "2017-02-24T06:41:52Z", + "update_time": "2017-02-24T06:41:52Z" + } +*/ + +export class Target { + id: number; + endpoint: string; + name: string; + username: string; + password: string; + type: number; + creation_time: Date; + update_time: Date; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/total-replication/total-replication.component.css b/src/ui_ng/src/app/replication/total-replication/total-replication.component.css new file mode 100644 index 000000000..23904eade --- /dev/null +++ b/src/ui_ng/src/app/replication/total-replication/total-replication.component.css @@ -0,0 +1,5 @@ +.option-right { + padding-right: 16px; + margin-top: 36px; + margin-bottom: 11px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/replication/total-replication/total-replication.component.html b/src/ui_ng/src/app/replication/total-replication/total-replication.component.html new file mode 100644 index 000000000..1236b7ecc --- /dev/null +++ b/src/ui_ng/src/app/replication/total-replication/total-replication.component.html @@ -0,0 +1,15 @@ +
+
+
+
+ + +
+ +
+ +
+
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/replication/total-replication/total-replication.component.ts b/src/ui_ng/src/app/replication/total-replication/total-replication.component.ts new file mode 100644 index 000000000..15b726e66 --- /dev/null +++ b/src/ui_ng/src/app/replication/total-replication/total-replication.component.ts @@ -0,0 +1,90 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ReplicationService } from '../../replication/replication.service'; + +import { CreateEditPolicyComponent } from '../../shared/create-edit-policy/create-edit-policy.component'; + +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +import { Policy } from '../../replication/policy'; + +@Component({ + selector: 'total-replication', + templateUrl: 'total-replication.component.html', + providers: [ ReplicationService ], + styleUrls: ['./total-replication.component.css'] +}) +export class TotalReplicationComponent implements OnInit { + + changedPolicies: Policy[]; + policies: Policy[]; + policyName: string = ''; + projectId: number; + + @ViewChild(CreateEditPolicyComponent) + createEditPolicyComponent: CreateEditPolicyComponent; + + constructor( + private replicationService: ReplicationService, + private messageHandlerService: MessageHandlerService) {} + + ngOnInit() { + this.retrievePolicies(); + } + + retrievePolicies(): void { + this.replicationService + .listPolicies(this.policyName) + .subscribe( + response=>{ + this.changedPolicies = response; + this.policies = this.changedPolicies; + }, + error=>this.messageHandlerService.handleError(error) + ); + } + + doSearchPolicies(policyName: string) { + this.policyName = policyName; + this.retrievePolicies(); + } + + openEditPolicy(policy: Policy) { + if(policy) { + let editable = true; + if(policy.enabled === 1) { + editable = false; + } + this.createEditPolicyComponent.openCreateEditPolicy(editable, policy.id); + } + } + + selectPolicy(policy: Policy) { + if(policy) { + this.projectId = policy.project_id; + } + } + + refreshPolicies() { + this.retrievePolicies(); + } + + reloadPolicies(isReady: boolean) { + if(isReady) { + this.policyName = ''; + this.retrievePolicies(); + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/list-repository/list-repository.component.html b/src/ui_ng/src/app/repository/list-repository/list-repository.component.html new file mode 100644 index 000000000..fbaa9e223 --- /dev/null +++ b/src/ui_ng/src/app/repository/list-repository/list-repository.component.html @@ -0,0 +1,17 @@ + + {{'REPOSITORY.NAME' | translate}} + {{'REPOSITORY.TAGS_COUNT' | translate}} + {{'REPOSITORY.PULL_COUNT' | translate}} + + + + + {{r.name || r.repository_name}} + {{r.tags_count}} + {{r.pull_count}} + + + {{totalRecordCount || (repositories ? repositories.length : 0)}} {{'REPOSITORY.ITEMS' | translate}} + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/list-repository/list-repository.component.ts b/src/ui_ng/src/app/repository/list-repository/list-repository.component.ts new file mode 100644 index 000000000..94b673bd2 --- /dev/null +++ b/src/ui_ng/src/app/repository/list-repository/list-repository.component.ts @@ -0,0 +1,64 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Repository } from '../repository'; +import { State } from 'clarity-angular'; + +import { SearchTriggerService } from '../../base/global-search/search-trigger.service'; + +@Component({ + selector: 'list-repository', + templateUrl: 'list-repository.component.html' +}) +export class ListRepositoryComponent implements OnInit { + + @Input() projectId: number; + @Input() repositories: Repository[]; + + + @Output() delete = new EventEmitter(); + + @Input() totalPage: number; + @Input() totalRecordCount: number; + @Output() paginate = new EventEmitter(); + + @Input() hasProjectAdminRole: boolean; + + pageOffset: number = 1; + + constructor( + private router: Router, + private searchTrigger: SearchTriggerService) { } + + ngOnInit() { } + + deleteRepo(repoName: string) { + this.delete.emit(repoName); + } + + refresh(state: State) { + if (this.repositories) { + this.paginate.emit(state); + } + } + + public gotoLink(projectId: number, repoName: string): void { + this.searchTrigger.closeSearch(true); + + let linkUrl = ['harbor', 'tags', projectId, repoName]; + this.router.navigate(linkUrl); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/repository.component.css b/src/ui_ng/src/app/repository/repository.component.css new file mode 100644 index 000000000..d101ae195 --- /dev/null +++ b/src/ui_ng/src/app/repository/repository.component.css @@ -0,0 +1,5 @@ +.option-right { + padding-right: 16px; + margin-top: 32px; + margin-bottom: 12px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/repository.component.html b/src/ui_ng/src/app/repository/repository.component.html new file mode 100644 index 000000000..bafef0007 --- /dev/null +++ b/src/ui_ng/src/app/repository/repository.component.html @@ -0,0 +1,13 @@ +
+
+
+
+ + +
+
+
+
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/repository/repository.component.ts b/src/ui_ng/src/app/repository/repository.component.ts new file mode 100644 index 000000000..87bf5631e --- /dev/null +++ b/src/ui_ng/src/app/repository/repository.component.ts @@ -0,0 +1,135 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { RepositoryService } from './repository.service'; +import { Repository } from './repository'; + +import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; +import { ConfirmationState, ConfirmationTargets } from '../shared/shared.const'; + + +import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service'; +import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message'; +import { Subscription } from 'rxjs/Subscription'; + +import { State } from 'clarity-angular'; + +import { Project } from '../project/project'; + +@Component({ + selector: 'repository', + templateUrl: 'repository.component.html', + styleUrls: ['./repository.component.css'] +}) +export class RepositoryComponent implements OnInit { + changedRepositories: Repository[]; + + projectId: number; + + lastFilteredRepoName: string; + + page: number = 1; + pageSize: number = 15; + + totalPage: number; + totalRecordCount: number; + + hasProjectAdminRole: boolean; + + subscription: Subscription; + + constructor( + private route: ActivatedRoute, + private repositoryService: RepositoryService, + private messageHandlerService: MessageHandlerService, + private deletionDialogService: ConfirmationDialogService + ) { + this.subscription = this.deletionDialogService + .confirmationConfirm$ + .subscribe( + message => { + if (message && + message.source === ConfirmationTargets.REPOSITORY && + message.state === ConfirmationState.CONFIRMED) { + let repoName = message.data; + this.repositoryService + .deleteRepository(repoName) + .subscribe( + response => { + this.refresh(); + this.messageHandlerService.showSuccess('REPOSITORY.DELETED_REPO_SUCCESS'); + console.log('Successful deleted repo:' + repoName); + }, + error => this.messageHandlerService.handleError(error) + ); + } + }); + + } + + ngOnInit(): void { + this.projectId = this.route.snapshot.parent.params['id']; + let resolverData = this.route.snapshot.parent.data; + if(resolverData) { + this.hasProjectAdminRole = (resolverData['projectResolver']).has_project_admin_role; + } + this.lastFilteredRepoName = ''; + this.retrieve(); + } + + ngOnDestroy(): void { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + retrieve(state?: State) { + if (state) { + this.page = state.page.to + 1; + } + this.repositoryService + .listRepositories(this.projectId, this.lastFilteredRepoName, this.page, this.pageSize) + .subscribe( + response => { + this.totalRecordCount = response.headers.get('x-total-count'); + this.totalPage = Math.ceil(this.totalRecordCount / this.pageSize); + console.log('TotalRecordCount:' + this.totalRecordCount + ', totalPage:' + this.totalPage); + this.changedRepositories = response.json(); + }, + error => this.messageHandlerService.handleError(error) + ); + } + + doSearchRepoNames(repoName: string) { + this.lastFilteredRepoName = repoName; + this.retrieve(); + + } + + deleteRepo(repoName: string) { + let message = new ConfirmationMessage( + 'REPOSITORY.DELETION_TITLE_REPO', + 'REPOSITORY.DELETION_SUMMARY_REPO', + repoName, + repoName, + ConfirmationTargets.REPOSITORY); + this.deletionDialogService.openComfirmDialog(message); + } + + refresh() { + this.retrieve(); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/repository.module.ts b/src/ui_ng/src/app/repository/repository.module.ts new file mode 100644 index 000000000..362ef58ed --- /dev/null +++ b/src/ui_ng/src/app/repository/repository.module.ts @@ -0,0 +1,40 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SharedModule } from '../shared/shared.module'; + +import { RepositoryComponent } from './repository.component'; +import { ListRepositoryComponent } from './list-repository/list-repository.component'; +import { TagRepositoryComponent } from './tag-repository/tag-repository.component'; +import { TopRepoComponent } from './top-repo/top-repo.component'; + +import { RepositoryService } from './repository.service'; + +@NgModule({ + imports: [ + SharedModule, + RouterModule + ], + declarations: [ + RepositoryComponent, + ListRepositoryComponent, + TagRepositoryComponent, + TopRepoComponent + ], + exports: [RepositoryComponent, ListRepositoryComponent, TopRepoComponent], + providers: [RepositoryService] +}) +export class RepositoryModule { } \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/repository.service.ts b/src/ui_ng/src/app/repository/repository.service.ts new file mode 100644 index 000000000..7d2de726a --- /dev/null +++ b/src/ui_ng/src/app/repository/repository.service.ts @@ -0,0 +1,93 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Http, URLSearchParams, Response } from '@angular/http'; + +import { Repository } from './repository'; +import { Tag } from './tag'; +import { VerifiedSignature } from './verified-signature'; + +import { Observable } from 'rxjs/Observable' +import 'rxjs/add/observable/of'; +import 'rxjs/add/operator/mergeMap'; + +@Injectable() +export class RepositoryService { + + constructor(private http: Http){} + + listRepositories(projectId: number, repoName: string, page?: number, pageSize?: number): Observable { + console.log('List repositories with project ID:' + projectId); + let params = new URLSearchParams(); + params.set('page', page + ''); + params.set('page_size', pageSize + ''); + return this.http + .get(`/api/repositories?project_id=${projectId}&q=${repoName}&detail=1`, {search: params}) + .map(response=>response) + .catch(error=>Observable.throw(error)); + } + + listTags(repoName: string): Observable { + return this.http + .get(`/api/repositories/${repoName}/tags?detail=1`) + .map(response=>response.json()) + .catch(error=>Observable.throw(error)); + } + + listNotarySignatures(repoName: string): Observable { + return this.http + .get(`/api/repositories/${repoName}/signatures`) + .map(response=>response.json()) + .catch(error=>Observable.throw(error)); + } + + listTagsWithVerifiedSignatures(repoName: string): Observable { + return this.listTags(repoName) + .map(res=>res) + .flatMap(tags=>{ + return this.listNotarySignatures(repoName).map(signatures=>{ + tags.forEach(t=>{ + for(let i = 0; i < signatures.length; i++) { + if(signatures[i].tag === t.tag) { + t.signed = true; + break; + } + } + }); + return tags; + }) + .catch(error=>{ + return tags; + }) + }) + .catch(error=>Observable.throw(error)); + } + + deleteRepository(repoName: string): Observable { + console.log('Delete repository with repo name:' + repoName); + return this.http + .delete(`/api/repositories/${repoName}/tags`) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + + deleteRepoByTag(repoName: string, tag: string): Observable { + console.log('Delete repository with repo name:' + repoName + ', tag:' + tag); + return this.http + .delete(`/api/repositories/${repoName}/tags/${tag}`) + .map(response=>response.status) + .catch(error=>Observable.throw(error)); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/repository.ts b/src/ui_ng/src/app/repository/repository.ts new file mode 100644 index 000000000..cc59c4b86 --- /dev/null +++ b/src/ui_ng/src/app/repository/repository.ts @@ -0,0 +1,45 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + { + "id": "2", + "name": "library/mysql", + "owner_id": 1, + "project_id": 1, + "description": "", + "pull_count": 0, + "star_count": 0, + "tags_count": 1, + "creation_time": "2017-02-14T09:22:58Z", + "update_time": "0001-01-01T00:00:00Z" + } +*/ + +export class Repository { + id: number; + name: string; + owner_id: number; + project_id: number; + description: string; + pull_count: number; + start_count: number; + tags_count: number; + creation_time: Date; + update_time: Date; + + constructor(name: string, tags_count: number) { + this.name = name; + this.tags_count = tags_count; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.css b/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.css new file mode 100644 index 000000000..6b0ece6bd --- /dev/null +++ b/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.css @@ -0,0 +1,3 @@ +.sub-header-title { + margin-top: 12px; +} diff --git a/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.html b/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.html new file mode 100644 index 000000000..c0e1acf8f --- /dev/null +++ b/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.html @@ -0,0 +1,46 @@ + +< {{'REPOSITORY.REPOSITORIES' | translate}} +< {{'SEARCH.BACK' | translate}} + + + + + + + +

{{repoName}}

+ + {{'REPOSITORY.TAG' | translate}} + {{'REPOSITORY.PULL_COMMAND' | translate}} + {{'REPOSITORY.SIGNED' | translate}} + {{'REPOSITORY.AUTHOR' | translate}} + {{'REPOSITORY.CREATED' | translate}} + {{'REPOSITORY.DOCKER_VERSION' | translate}} + {{'REPOSITORY.ARCHITECTURE' | translate}} + {{'REPOSITORY.OS' | translate}} + + + + + + + {{t.tag}} + {{t.pullCommand}} + + + + + {{t.author}} + {{t.created | date: 'short'}} + {{t.dockerVersion}} + {{t.architecture}} + {{t.os}} + + {{tags ? tags.length : 0}} {{'REPOSITORY.ITEMS' | translate}} + \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.ts b/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.ts new file mode 100644 index 000000000..017d3de0f --- /dev/null +++ b/src/ui_ng/src/app/repository/tag-repository/tag-repository.component.ts @@ -0,0 +1,193 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { RepositoryService } from '../repository.service'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; +import { ConfirmationTargets, ConfirmationState } from '../../shared/shared.const'; + +import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service'; +import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message'; + +import { Subscription } from 'rxjs/Subscription'; + +import { Tag } from '../tag'; +import { TagView } from '../tag-view'; + +import { AppConfigService } from '../../app-config.service'; + +import { SessionService } from '../../shared/session.service'; + +import { Project } from '../../project/project'; + +@Component({ + selector: 'tag-repository', + templateUrl: 'tag-repository.component.html', + styleUrls: ['./tag-repository.component.css'] +}) +export class TagRepositoryComponent implements OnInit, OnDestroy { + + projectId: number; + repoName: string; + + hasProjectAdminRole: boolean = false; + + tags: TagView[]; + registryUrl: string; + withNotary: boolean; + + hasSignedIn: boolean; + + showTagManifestOpened: boolean; + manifestInfoTitle: string; + tagID: string; + staticBackdrop: boolean = true; + closable: boolean = false; + + selectAll: boolean = false; + + private subscription: Subscription; + + constructor( + private route: ActivatedRoute, + private messageHandlerService: MessageHandlerService, + private deletionDialogService: ConfirmationDialogService, + private repositoryService: RepositoryService, + private appConfigService: AppConfigService, + private session: SessionService){ + + this.subscription = this.deletionDialogService.confirmationConfirm$.subscribe( + message => { + if (message && + message.source === ConfirmationTargets.TAG + && message.state === ConfirmationState.CONFIRMED) { + let tag = message.data; + if (tag) { + if (tag.signed) { + return; + } else { + let tagName = tag.tag; + this.repositoryService + .deleteRepoByTag(this.repoName, tagName) + .subscribe( + response => { + this.retrieve(); + this.messageHandlerService.showSuccess('REPOSITORY.DELETED_TAG_SUCCESS'); + console.log('Deleted repo:' + this.repoName + ' with tag:' + tagName); + }, + error => this.messageHandlerService.handleError(error) + ); + } + } + } + }); + } + + ngOnInit() { + this.hasSignedIn = (this.session.getCurrentUser() !== null); + let resolverData = this.route.snapshot.data; + if(resolverData) { + this.hasProjectAdminRole = (resolverData['projectResolver']).has_project_admin_role; + } + this.projectId = this.route.snapshot.params['id']; + this.repoName = this.route.snapshot.params['repo']; + this.tags = []; + this.registryUrl = this.appConfigService.getConfig().registry_url; + this.withNotary = this.appConfigService.getConfig().with_notary; + this.retrieve(); + } + + ngOnDestroy() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + retrieve() { + this.tags = []; + if(this.withNotary) { + this.repositoryService + .listTagsWithVerifiedSignatures(this.repoName) + .subscribe( + items => this.listTags(items), + error => this.messageHandlerService.handleError(error)); + } else { + this.repositoryService + .listTags(this.repoName) + .subscribe( + items => this.listTags(items), + error => this.messageHandlerService.handleError(error)); + } + } + + private listTags(tags: Tag[]): void { + tags.forEach(t => { + let tag = new TagView(); + tag.tag = t.tag; + let data = JSON.parse(t.manifest.history[0].v1Compatibility); + tag.architecture = data['architecture']; + tag.author = data['author']; + tag.signed = t.signed; + tag.created = data['created']; + tag.dockerVersion = data['docker_version']; + tag.pullCommand = 'docker pull ' + this.registryUrl + '/' + t.manifest.name + ':' + t.tag; + tag.os = data['os']; + tag.id = data['id']; + tag.parent = data['parent']; + this.tags.push(tag); + }); + } + + deleteTag(tag: TagView) { + if (tag) { + let titleKey: string, summaryKey: string, content: string, confirmOnly: boolean; + if (tag.signed) { + titleKey = 'REPOSITORY.DELETION_TITLE_TAG_DENIED'; + summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG_DENIED'; + confirmOnly = true; + content = 'notary -s https://' + this.registryUrl + ':4443 -d ~/.docker/trust remove -p ' + this.registryUrl + '/' + this.repoName + ' ' + tag.tag; + } else { + titleKey = 'REPOSITORY.DELETION_TITLE_TAG'; + summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG'; + content = tag.tag; + confirmOnly = false; + } + let message = new ConfirmationMessage( + titleKey, + summaryKey, + content, + tag, + ConfirmationTargets.TAG); + message.confirmOnly = confirmOnly; + this.deletionDialogService.openComfirmDialog(message); + } + } + + showTagID(type: string, tag: TagView) { + if(tag) { + if(type === 'tag') { + this.manifestInfoTitle = 'REPOSITORY.COPY_ID'; + this.tagID = tag.id; + } else if(type === 'parent') { + this.manifestInfoTitle = 'REPOSITORY.COPY_PARENT_ID'; + this.tagID = tag.parent; + } + this.showTagManifestOpened = true; + } + } + selectAndCopy($event) { + $event.target.select(); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/tag-view.ts b/src/ui_ng/src/app/repository/tag-view.ts new file mode 100644 index 000000000..63d5523b0 --- /dev/null +++ b/src/ui_ng/src/app/repository/tag-view.ts @@ -0,0 +1,25 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export class TagView { + tag: string; + pullCommand: string; + signed: boolean; + author: string; + created: Date; + dockerVersion: string; + architecture: string; + os: string; + id: string; + parent: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/tag.ts b/src/ui_ng/src/app/repository/tag.ts new file mode 100644 index 000000000..5a7b70979 --- /dev/null +++ b/src/ui_ng/src/app/repository/tag.ts @@ -0,0 +1,40 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* + { + "tag": "latest", + "manifest": { + "schemaVersion": 1, + "name": "library/photon", + "tag": "latest", + "architecture": "amd64", + "history": [] + }, + +*/ +export class Tag { + tag: string; + manifest: { + schemaVersion: number; + name: string; + tag: string; + architecture: string; + history: [ + { + v1Compatibility: string; + } + ]; + }; + signed: boolean; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/top-repo/top-repo.component.css b/src/ui_ng/src/app/repository/top-repo/top-repo.component.css new file mode 100644 index 000000000..10568fe6d --- /dev/null +++ b/src/ui_ng/src/app/repository/top-repo/top-repo.component.css @@ -0,0 +1,4 @@ +.repo-wrapper { + margin-left: 48px; + margin-right: 48px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/top-repo/top-repo.component.html b/src/ui_ng/src/app/repository/top-repo/top-repo.component.html new file mode 100644 index 000000000..c5a2825fd --- /dev/null +++ b/src/ui_ng/src/app/repository/top-repo/top-repo.component.html @@ -0,0 +1,8 @@ +
+
+

{{'REPOSITORY.POP_REPOS' | translate}}

+
+
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/repository/top-repo/top-repo.component.ts b/src/ui_ng/src/app/repository/top-repo/top-repo.component.ts new file mode 100644 index 000000000..f53d3a98b --- /dev/null +++ b/src/ui_ng/src/app/repository/top-repo/top-repo.component.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit } from '@angular/core'; + +import { errorHandler } from '../../shared/shared.utils'; +import { AlertType, ListMode } from '../../shared/shared.const'; +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; +import { TopRepoService } from './top-repository.service'; +import { Repository } from '../repository'; + +@Component({ + selector: 'top-repo', + templateUrl: "top-repo.component.html", + styleUrls: ['top-repo.component.css'], + + providers: [TopRepoService] +}) +export class TopRepoComponent implements OnInit{ + private topRepos: Repository[] = []; + + constructor( + private topRepoService: TopRepoService, + private messageHandlerService: MessageHandlerService + ) { } + + public get listMode(): string { + return ListMode.READONLY; + } + + //Implement ngOnIni + ngOnInit(): void { + this.getTopRepos(); + } + + //Get top popular repositories + getTopRepos() { + this.topRepoService.getTopRepos() + .then(repos => this.topRepos = repos ) + .catch(error => { + this.messageHandlerService.handleError(error); + }) + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/top-repo/top-repository.service.ts b/src/ui_ng/src/app/repository/top-repo/top-repository.service.ts new file mode 100644 index 000000000..da445bcbb --- /dev/null +++ b/src/ui_ng/src/app/repository/top-repo/top-repository.service.ts @@ -0,0 +1,52 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { Repository } from '../repository'; + +export const topRepoEndpoint = "/api/repositories/top?detail=1"; +/** + * Declare service to handle the top repositories + * + * + * @export + * @class GlobalSearchService + */ +@Injectable() +export class TopRepoService { + private headers = new Headers({ + "Content-Type": 'application/json' + }); + private options = new RequestOptions({ + headers: this.headers + }); + + constructor(private http: Http) { } + + /** + * Get top popular repositories + * + * @param {string} keyword + * @returns {Promise} + * + * @memberOf GlobalSearchService + */ + getTopRepos(): Promise { + return this.http.get(topRepoEndpoint, this.options).toPromise() + .then(response => response.json() as Repository[]) + .catch(error => Promise.reject(error)); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/top-repo/top-repository.ts b/src/ui_ng/src/app/repository/top-repo/top-repository.ts new file mode 100644 index 000000000..f17fc29cb --- /dev/null +++ b/src/ui_ng/src/app/repository/top-repo/top-repository.ts @@ -0,0 +1,17 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export class TopRepo { + name: string; + count: number; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/repository/verified-signature.ts b/src/ui_ng/src/app/repository/verified-signature.ts new file mode 100644 index 000000000..447ce17e9 --- /dev/null +++ b/src/ui_ng/src/app/repository/verified-signature.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/* +[ + { + "tag": "2.0", + "hashes": { + "sha256": "E1lggRW5RZnlZBY4usWu8d36p5u5YFfr9B68jTOs+Kc=" + } + } +] +*/ + +export class VerifiedSignature { + tag: string; + hashes: { + sha256: string; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.css b/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.css new file mode 100644 index 000000000..cb14978a2 --- /dev/null +++ b/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.css @@ -0,0 +1,14 @@ +.margin-left-override { + margin-left: 24px !important; +} + +.about-version { + font-size: 14px; + color: #565656; + font-weight: 500; +} + +.about-build { + font-size: 14px; + color: #565656; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.html b/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.html new file mode 100644 index 000000000..c727a38be --- /dev/null +++ b/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.html @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.ts b/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.ts new file mode 100644 index 000000000..bef9474ee --- /dev/null +++ b/src/ui_ng/src/app/shared/about-dialog/about-dialog.component.ts @@ -0,0 +1,41 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component } from '@angular/core'; + +import { AppConfigService } from '../../app-config.service'; + +@Component({ + selector: 'about-dialog', + templateUrl: "about-dialog.component.html", + styleUrls: ["about-dialog.component.css"] +}) +export class AboutDialogComponent { + private opened: boolean = false; + private build: string = "4276418"; + + constructor(private appConfigService: AppConfigService) { } + + public get version(): string { + let appConfig = this.appConfigService.getConfig(); + return appConfig?appConfig.harbor_version: "n/a"; + } + + public open(): void { + this.opened = true; + } + + public close(): void { + this.opened = false; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.css b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.css new file mode 100644 index 000000000..4f6c2933e --- /dev/null +++ b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.css @@ -0,0 +1,19 @@ +.confirmation-icon-inline { + display: inline-block; +} + +.confirmation-title { + line-height: 24px; + color: #000000; + font-size: 22px; +} + +.confirmation-content { + font-size: 14px; + color: #565656; + line-height: 24px; + display: inline-block; + vertical-align: middle; + width: 80%; + white-space: pre-wrap; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.html b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.html new file mode 100644 index 000000000..fe56db9b8 --- /dev/null +++ b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.html @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.ts b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.ts new file mode 100644 index 000000000..a057122db --- /dev/null +++ b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.component.ts @@ -0,0 +1,99 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs/Subscription'; +import { TranslateService } from '@ngx-translate/core'; + +import { ConfirmationDialogService } from './confirmation-dialog.service'; +import { ConfirmationMessage } from './confirmation-message'; +import { ConfirmationAcknowledgement } from './confirmation-state-message'; +import { ConfirmationState, ConfirmationTargets } from '../shared.const'; + +@Component({ + selector: 'confiramtion-dialog', + templateUrl: 'confirmation-dialog.component.html', + styleUrls: ['confirmation-dialog.component.css'] +}) + +export class ConfirmationDialogComponent implements OnDestroy { + opened: boolean = false; + dialogTitle: string = ""; + dialogContent: string = ""; + buttonKey: string = 'BUTTON.OK'; + confirmOnly: boolean = false; + message: ConfirmationMessage; + private annouceSubscription: Subscription; + + constructor( + private confirmationService: ConfirmationDialogService, + private translate: TranslateService) { + this.annouceSubscription = confirmationService.confirmationAnnouced$.subscribe(msg => { + this.dialogTitle = msg.title; + this.dialogContent = msg.message; + this.message = msg; + this.confirmOnly = this.message.confirmOnly; + this.buttonKey = this.confirmOnly ? 'BUTTON.CLOSE' : 'BUTTON.OK'; + this.translate.get(this.dialogTitle).subscribe((res: string) => this.dialogTitle = res); + this.translate.get(this.dialogContent, { 'param': msg.param }).subscribe((res: string) => this.dialogContent = res); + //Open dialog + this.open(); + }); + } + + ngOnDestroy(): void { + if (this.annouceSubscription) { + this.annouceSubscription.unsubscribe(); + } + } + + open(): void { + this.opened = true; + } + + close(): void { + this.opened = false; + } + + cancel(): void { + if(!this.message){//Inproper condition + this.close(); + return; + } + + let data: any = this.message.data ? this.message.data : {}; + let target = this.message.targetId ? this.message.targetId : ConfirmationTargets.EMPTY; + this.confirmationService.cancel(new ConfirmationAcknowledgement( + ConfirmationState.CANCEL, + data, + target + )); + this.close(); + } + + confirm(): void { + if(!this.message){//Inproper condition + this.close(); + return; + } + + let data: any = this.message.data ? this.message.data : {}; + let target = this.message.targetId ? this.message.targetId : ConfirmationTargets.EMPTY; + this.confirmationService.confirm(new ConfirmationAcknowledgement( + ConfirmationState.CONFIRMED, + data, + target + )); + this.close(); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.service.ts b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.service.ts new file mode 100644 index 000000000..f8c232add --- /dev/null +++ b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-dialog.service.ts @@ -0,0 +1,43 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core' +import { Subject } from 'rxjs/Subject'; + +import { ConfirmationMessage } from './confirmation-message'; +import { ConfirmationState } from '../shared.const'; +import { ConfirmationAcknowledgement } from './confirmation-state-message'; + +@Injectable() +export class ConfirmationDialogService { + private confirmationAnnoucedSource = new Subject(); + private confirmationConfirmSource = new Subject(); + + confirmationAnnouced$ = this.confirmationAnnoucedSource.asObservable(); + confirmationConfirm$ = this.confirmationConfirmSource.asObservable(); + + //User confirm the action + public confirm(ack: ConfirmationAcknowledgement): void { + this.confirmationConfirmSource.next(ack); + } + + //User cancel the action + public cancel(ack: ConfirmationAcknowledgement): void { + this.confirm(ack); + } + + //Open the confirmation dialog + public openComfirmDialog(message: ConfirmationMessage): void { + this.confirmationAnnoucedSource.next(message); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-message.ts b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-message.ts new file mode 100644 index 000000000..15d1fcad8 --- /dev/null +++ b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-message.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { ConfirmationTargets } from '../../shared/shared.const'; + +export class ConfirmationMessage { + public constructor(title: string, message: string, param: string, data: any, targetId: ConfirmationTargets) { + this.title = title; + this.message = message; + this.data = data; + this.targetId = targetId; + this.param = param; + } + title: string; + message: string; + data: any = {};//default is empty + targetId: ConfirmationTargets = ConfirmationTargets.EMPTY; + param: string; + confirmOnly: boolean; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-state-message.ts b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-state-message.ts new file mode 100644 index 000000000..0837d3931 --- /dev/null +++ b/src/ui_ng/src/app/shared/confirmation-dialog/confirmation-state-message.ts @@ -0,0 +1,26 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { ConfirmationState, ConfirmationTargets } from '../shared.const'; + +export class ConfirmationAcknowledgement { + constructor(state: ConfirmationState, data: any, source: ConfirmationTargets) { + this.state = state; + this.data = data; + this.source = source; + } + + state: ConfirmationState = ConfirmationState.NA; + data: any = {}; + source: ConfirmationTargets = ConfirmationTargets.EMPTY; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.css b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.css new file mode 100644 index 000000000..0bcac82e9 --- /dev/null +++ b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.css @@ -0,0 +1,4 @@ +.form-group-label-override { + font-size: 14px; + font-weight: 400; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.html b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.html new file mode 100644 index 000000000..abcb82184 --- /dev/null +++ b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.html @@ -0,0 +1,82 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.ts b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.ts new file mode 100644 index 000000000..e74c09e6e --- /dev/null +++ b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.component.ts @@ -0,0 +1,350 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, Output, EventEmitter, OnInit, ViewChild, AfterViewChecked } from '@angular/core'; + +import { NgForm } from '@angular/forms'; + +import { CreateEditPolicy } from './create-edit-policy'; + +import { ReplicationService } from '../../replication/replication.service'; +import { MessageHandlerService } from '../message-handler/message-handler.service'; +import { ActionType } from '../../shared/shared.const'; + +import { InlineAlertComponent } from '../../shared/inline-alert/inline-alert.component'; + +import { Policy } from '../../replication/policy'; +import { Target } from '../../replication/target'; + +import { TranslateService } from '@ngx-translate/core'; + +const FAKE_PASSWORD: string = 'ywJZnDTM'; + +@Component({ + selector: 'create-edit-policy', + templateUrl: 'create-edit-policy.component.html', + styleUrls: [ 'create-edit-policy.component.css' ] +}) +export class CreateEditPolicyComponent implements OnInit, AfterViewChecked { + + modalTitle: string; + createEditPolicyOpened: boolean; + createEditPolicy: CreateEditPolicy = new CreateEditPolicy(); + initVal: CreateEditPolicy = new CreateEditPolicy(); + + actionType: ActionType; + + isCreateDestination: boolean; + @Input() projectId: number; + + @Output() reload = new EventEmitter(); + + targets: Target[]; + + pingTestMessage: string; + testOngoing: boolean; + pingStatus: boolean; + + policyForm: NgForm; + + staticBackdrop: boolean = true; + closable: boolean = false; + + @ViewChild('policyForm') + currentForm: NgForm; + + hasChanged: boolean; + + editable: boolean; + + @ViewChild(InlineAlertComponent) + inlineAlert: InlineAlertComponent; + + get readonly(): boolean { + return this.actionType === ActionType.EDIT && this.createEditPolicy.enable; + } + + get untoggleable(): boolean { + return this.actionType === ActionType.EDIT && this.initVal.enable; + } + + get showNewDestination(): boolean { + return this.actionType === ActionType.ADD_NEW || !this.createEditPolicy.enable; + } + + constructor( + private replicationService: ReplicationService, + private messageHandlerService: MessageHandlerService, + private translateService: TranslateService) {} + + prepareTargets(targetId?: number) { + this.replicationService + .listTargets('') + .subscribe( + targets=>{ + this.targets = targets; + if(this.targets && this.targets.length > 0) { + let initialTarget: Target; + (targetId) ? initialTarget = this.targets.find(t=>t.id==targetId) : initialTarget = this.targets[0]; + this.createEditPolicy.targetId = initialTarget.id; + this.createEditPolicy.targetName = initialTarget.name; + this.createEditPolicy.endpointUrl = initialTarget.endpoint; + this.createEditPolicy.username = initialTarget.username; + this.createEditPolicy.password = FAKE_PASSWORD; + + this.initVal.targetId = this.createEditPolicy.targetId; + this.initVal.endpointUrl = this.createEditPolicy.endpointUrl; + this.initVal.username = this.createEditPolicy.username; + this.initVal.password = this.createEditPolicy.password; + } + }, + error=>{ + this.messageHandlerService.handleError(error); + this.createEditPolicyOpened = false; + } + ); + } + + ngOnInit(): void {} + + openCreateEditPolicy(editable: boolean, policyId?: number): void { + this.createEditPolicyOpened = true; + this.createEditPolicy = new CreateEditPolicy(); + + this.editable = editable; + + this.isCreateDestination = false; + + this.hasChanged = false; + + this.pingTestMessage = ''; + this.pingStatus = true; + this.testOngoing = false; + + if(policyId) { + this.actionType = ActionType.EDIT; + this.translateService.get('REPLICATION.EDIT_POLICY_TITLE').subscribe(res=>this.modalTitle=res); + this.replicationService + .getPolicy(policyId) + .subscribe( + policy=>{ + this.createEditPolicy.policyId = policyId; + this.createEditPolicy.name = policy.name; + this.createEditPolicy.description = policy.description; + this.createEditPolicy.enable = policy.enabled === 1? true : false; + this.prepareTargets(policy.target_id); + + this.initVal.name = this.createEditPolicy.name; + this.initVal.description = this.createEditPolicy.description; + this.initVal.enable = this.createEditPolicy.enable; + } + ) + } else { + this.actionType = ActionType.ADD_NEW; + this.translateService.get('REPLICATION.ADD_POLICY').subscribe(res=>this.modalTitle=res); + this.prepareTargets(); + } + } + + newDestination(checkedAddNew: boolean): void { + console.log('CheckedAddNew:' + checkedAddNew); + this.isCreateDestination = checkedAddNew; + if(this.isCreateDestination) { + this.createEditPolicy.targetName = ''; + this.createEditPolicy.endpointUrl = ''; + this.createEditPolicy.username = ''; + this.createEditPolicy.password = ''; + } else { + this.prepareTargets(); + } + } + + selectTarget(): void { + let result = this.targets.find(target=>target.id == this.createEditPolicy.targetId); + if(result) { + this.createEditPolicy.targetId = result.id; + this.createEditPolicy.endpointUrl = result.endpoint; + this.createEditPolicy.username = result.username; + this.createEditPolicy.password = FAKE_PASSWORD; + } + } + + getPolicyByForm(): Policy { + let policy = new Policy(); + policy.project_id = this.projectId; + policy.id = this.createEditPolicy.policyId; + policy.name = this.createEditPolicy.name; + policy.description = this.createEditPolicy.description; + policy.enabled = this.createEditPolicy.enable ? 1 : 0; + policy.target_id = this.createEditPolicy.targetId; + return policy; + } + + getTargetByForm(): Target { + let target = new Target(); + target.id = this.createEditPolicy.targetId; + target.name = this.createEditPolicy.targetName; + target.endpoint = this.createEditPolicy.endpointUrl; + target.username = this.createEditPolicy.username; + target.password = this.createEditPolicy.password; + return target; + } + + createPolicy(): void { + console.log('Create policy with existing target in component.'); + this.replicationService + .createPolicy(this.getPolicyByForm()) + .subscribe( + response=>{ + this.messageHandlerService.showSuccess('REPLICATION.CREATED_SUCCESS'); + console.log('Successful created policy: ' + response); + this.createEditPolicyOpened = false; + this.reload.emit(true); + }, + error=>{ + if(this.messageHandlerService.isAppLevel(error)) { + this.messageHandlerService.handleError(error); + this.createEditPolicyOpened = false; + } else if (error.status === 409) { + this.inlineAlert.showInlineError('REPLICATION.POLICY_ALREADY_EXISTS'); + } else { + this.inlineAlert.showInlineError(error); + } + console.log('Failed to create policy:' + error.status + ', error message:' + JSON.stringify(error['_body'])); + }); + } + + createOrUpdatePolicyAndCreateTarget(): void { + console.log('Creating policy with new created target.'); + this.replicationService + .createOrUpdatePolicyWithNewTarget(this.getPolicyByForm(), this.getTargetByForm()) + .subscribe( + response=>{ + this.messageHandlerService.showSuccess('REPLICATION.CREATED_SUCCESS'); + console.log('Successful created policy and target:' + response); + this.createEditPolicyOpened = false; + this.reload.emit(true); + }, + error=>{ + if(this.messageHandlerService.isAppLevel(error)) { + this.messageHandlerService.handleError(error); + this.createEditPolicyOpened = false; + } else if (error.status === 409) { + this.inlineAlert.showInlineError('REPLICATION.POLICY_ALREADY_EXISTS'); + } else { + this.inlineAlert.showInlineError(error); + } + console.log('Failed to create policy and target:' + error.status + ', error message:' + JSON.stringify(error['_body'])); + } + ); + } + + updatePolicy(): void { + console.log('Creating policy with existing target.'); + this.replicationService + .updatePolicy(this.getPolicyByForm()) + .subscribe( + response=>{ + console.log('Successful created policy and target:' + response); + this.messageHandlerService.showSuccess('REPLICATION.UPDATED_SUCCESS') + this.createEditPolicyOpened = false; + this.reload.emit(true); + }, + error=>{ + if(this.messageHandlerService.isAppLevel(error)) { + this.messageHandlerService.handleError(error); + this.createEditPolicyOpened = false; + } else if (error.status === 409) { + this.inlineAlert.showInlineError('REPLICATION.POLICY_ALREADY_EXISTS'); + } else { + this.inlineAlert.showInlineError(error); + } + console.log('Failed to create policy and target:' + error.status + ', error message:' + JSON.stringify(error['_body'])); + } + ); + } + + onSubmit() { + if(this.isCreateDestination) { + this.createOrUpdatePolicyAndCreateTarget(); + } else { + if(this.actionType === ActionType.ADD_NEW) { + this.createPolicy(); + } else if(this.actionType === ActionType.EDIT){ + this.updatePolicy(); + } + } + } + + onCancel() { + if(this.hasChanged) { + this.inlineAlert.showInlineConfirmation({message: 'ALERT.FORM_CHANGE_CONFIRMATION'}); + } else { + this.createEditPolicyOpened = false; + this.policyForm.reset(); + } + } + + confirmCancel(confirmed: boolean) { + this.createEditPolicyOpened = false; + this.inlineAlert.close(); + this.policyForm.reset(); + } + + ngAfterViewChecked(): void { + this.policyForm = this.currentForm; + if(this.policyForm) { + this.policyForm.valueChanges.subscribe(data=>{ + for(let i in data) { + let origin = this.initVal[i]; + let current = data[i]; + if(((this.actionType === ActionType.EDIT && !this.readonly && !current ) || current) && current !== origin) { + this.hasChanged = true; + break; + } else { + this.hasChanged = false; + this.inlineAlert.close(); + } + } + }); + } + } + + testConnection() { + this.pingStatus = true; + this.translateService.get('REPLICATION.TESTING_CONNECTION').subscribe(res=>this.pingTestMessage=res); + this.testOngoing = !this.testOngoing; + let pingTarget: Target | any = {}; + if(this.isCreateDestination) { + pingTarget.endpoint = this.createEditPolicy.endpointUrl; + pingTarget.username = this.createEditPolicy.username; + pingTarget.password = this.createEditPolicy.password; + } else { + pingTarget.id = this.createEditPolicy.targetId; + } + this.replicationService + .pingTarget(pingTarget) + .subscribe( + response=>{ + this.testOngoing = !this.testOngoing; + this.translateService.get('REPLICATION.TEST_CONNECTION_SUCCESS').subscribe(res=>this.pingTestMessage=res); + this.pingStatus = true; + }, + error=>{ + this.testOngoing = !this.testOngoing; + this.translateService.get('REPLICATION.TEST_CONNECTION_FAILURE').subscribe(res=>this.pingTestMessage=res); + this.pingStatus = false; + } + ); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.ts b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.ts new file mode 100644 index 000000000..9b43a994d --- /dev/null +++ b/src/ui_ng/src/app/shared/create-edit-policy/create-edit-policy.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export class CreateEditPolicy { + policyId: number; + name: string; + description: string; + enable: boolean; + targetId: number; + targetName: string; + endpointUrl: string; + username: string; + password: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/email.directive.ts b/src/ui_ng/src/app/shared/email.directive.ts new file mode 100644 index 000000000..d58c34417 --- /dev/null +++ b/src/ui_ng/src/app/shared/email.directive.ts @@ -0,0 +1,46 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Directive } from '@angular/core'; +import { ValidatorFn, AbstractControl, Validator, NG_VALIDATORS, Validators } from '@angular/forms'; + +const emailPattern = /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + +export function emailValidator(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const value: string = control.value + if (!value) { + return { 'email': false }; + } + + const regExp = new RegExp(emailPattern); + if(!regExp.test(value)){ + return { 'email': false }; + } + + return null; + } +} + +@Directive({ + selector: '[email]', + providers: [{ provide: NG_VALIDATORS, useExisting: EmailValidatorDirective, multi: true }] +}) + +export class EmailValidatorDirective implements Validator { + valFn = emailValidator(); + + validate(control: AbstractControl): { [key: string]: any } { + return this.valFn(control); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/filter/filter.component.css b/src/ui_ng/src/app/shared/filter/filter.component.css new file mode 100644 index 000000000..98d8eccf5 --- /dev/null +++ b/src/ui_ng/src/app/shared/filter/filter.component.css @@ -0,0 +1,4 @@ +.filter-icon { + position: relative; + right: -12px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/filter/filter.component.html b/src/ui_ng/src/app/shared/filter/filter.component.html new file mode 100644 index 000000000..302b00362 --- /dev/null +++ b/src/ui_ng/src/app/shared/filter/filter.component.html @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/filter/filter.component.ts b/src/ui_ng/src/app/shared/filter/filter.component.ts new file mode 100644 index 000000000..7a31d6bf0 --- /dev/null +++ b/src/ui_ng/src/app/shared/filter/filter.component.ts @@ -0,0 +1,55 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, Output, OnInit, EventEmitter } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { Observable } from 'rxjs/Observable'; + +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; + + +@Component({ + selector: 'grid-filter', + templateUrl: 'filter.component.html', + styleUrls: ['filter.component.css'] +}) + +export class FilterComponent implements OnInit { + + private placeHolder: string = ""; + private filterTerms = new Subject(); + + @Output("filter") private filterEvt = new EventEmitter(); + + @Input() currentValue; + @Input("filterPlaceholder") + public set flPlaceholder(placeHolder: string) { + this.placeHolder = placeHolder; + } + + ngOnInit(): void { + this.filterTerms + .debounceTime(500) + //.distinctUntilChanged() + .subscribe(terms => { + this.filterEvt.emit(terms); + }); + + } + + valueChange(): void { + //Send out filter terms + this.filterTerms.next(this.currentValue.trim()); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/gauge/gauge.component.css b/src/ui_ng/src/app/shared/gauge/gauge.component.css new file mode 100644 index 000000000..ab2bfc2ab --- /dev/null +++ b/src/ui_ng/src/app/shared/gauge/gauge.component.css @@ -0,0 +1,300 @@ +.esxc-gauge-container { + position: relative; + overflow: hidden; +} + + +/* + * Gauge track color + * + * TODO: we should make this configurable in the directive + */ + +.esxc-gauge-circle-bg { + background-color: #EEE; +} + + +/* + * Gauge bar + */ + +.esxc-gauge-circle-fill { + position: absolute; + top: 0; + left: 0; + right: 0; + background: transparent; + transform: rotate(0deg); +} + + +/* + * Gauge center area + */ + +.esxc-gauge-circle-inner { + position: absolute; + top: 0; + left: 0; +} + + +/* + * Gauge caption + */ + +.esxc-gauge-circle-caption { + text-align: center; +} + +.esxc-gauge-bar-one { + opacity: 0.5; +} + + +/* + * Small size gauge sizing + */ + +.esxc-gauge-small .esxc-gauge-container { + width: 60px; + height: 30px; + top: -8px; + left: 4px; +} + +.esxc-gauge-small .esxc-gauge-circle-fill { + height: 60px; + border-radius: 30px; + clip: rect(30px, 60px, 60px, 0); +} + +.esxc-gauge-small .esxc-gauge-circle-bg { + height: 60px; + border-radius: 30px; +} + +.esxc-gauge-small .esxc-gauge-circle-inner { + width: 50px; + height: 50px; + border-radius: 25px; + margin: 5px; +} + +.esxc-gauge-small .esxc-gauge-circle-caption { + margin-top: 6px; +} + + +/* + * Medium size gauge sizing + */ + +.esxc-gauge-medium .esxc-gauge-container { + width: 130px; + height: 65px; +} + +.esxc-gauge-medium .esxc-gauge-circle-fill { + height: 130px; + border-radius: 130px; + clip: rect(65px, 130px, 130px, 0); +} + +.esxc-gauge-medium .esxc-gauge-circle-bg { + height: 130px; + border-radius: 65px; +} + +.esxc-gauge-medium .esxc-gauge-circle-inner { + height: 120px; + width: 120px; + border-radius: 60px; + margin: 5px; +} + + +/* + * Large size gauge sizing + */ + +.esxc-gauge-large .esxc-gauge-container { + width: 260px; + height: 130px; +} + +.esxc-gauge-large .esxc-gauge-circle-fill { + height: 260px; + border-radius: 130px; + clip: rect(130px, 260px, 260px, 0); +} + +.esxc-gauge-large .esxc-gauge-circle-bg { + height: 260px; + border-radius: 130px; +} + +.esxc-gauge-large .esxc-gauge-circle-inner { + width: 240px; + height: 240px; + border-radius: 120px; + margin: 10px; +} + +.esxc-gauge-large .esxc-gauge-circle-caption { + margin-top: 95px; +} + + +/*Sall size*/ + +.esxc-gauge-small .esxc-gauge-circle-caption .esxc-value { + font-size: 16px; + font-weight: 600; +} + +.esxc-gauge-small .esxc-gauge-circle-caption .esxc-unit { + font-size: 10px; +} + +.esxc-gauge-small .esxc-gauge-circle-caption .esxc-loading { + font-size: 10px; +} + +.esxc-gauge-small .esxc-title { + font-size: 10px; + text-align: center; + margin-top: 3px; + position: relative; + bottom: -2px; +} + +.esxc-gauge-small .esxc-title .esxc-bar-title { + float: left; + text-align: right; + width: 70px; + margin-top: 0; +} + +.esxc-gauge-small .esxc-limit { + text-align: center; + position: relative; + top: -8px; + width: 70px; +} + +.esxc-gauge-small .esxc-limit .esxc-value { + font-size: 8px; +} + +.esxc-gauge-small .esxc-limit .esxc-unit { + font-size: 8px; +} + +.esxc-gauge-small .esxc-limit .esxc-label { + font-size: 8px; +} + +.esxc-gauge-small .esxc-limit .esxc-bar-limit { + text-align: right; +} + + +/*medium size*/ + +.esxc-gauge-medium .esxc-gauge-circle-caption { + margin-top: 40px; + color: #000; +} + +.esxc-gauge-medium .esxc-gauge-circle-caption .esxc-value { + font-size: 22px; +} + +.esxc-gauge-medium .esxc-gauge-circle-caption .esxc-unit { + font-size: 14px; +} + +.esxc-gauge-medium .esxc-gauge-circle-caption .esxc-loading { + font-size: 25px; +} + +.esxc-gauge-medium .esxc-limit { + text-align: center; + color: #565656; +} + +.esxc-gauge-medium .esxc-limit .esxc-value { + font-size: 12px; +} + +.esxc-gauge-medium .esxc-limit .esxc-unit { + font-size: 12px; +} + +.esxc-gauge-medium .esxc-limit .esxc-label { + font-size: 12px; +} + +.esxc-gauge-medium .esxc-title { + font-size: 13px; + font-weight: 600; + color: #565656; + text-align: center; + margin-top: -8px; +} + + +/*large size*/ + +.esxc-gauge-large .esxc-gauge-circle-caption .esxc-value { + font-size: 40px; +} + +.esxc-gauge-large .esxc-gauge-circle-caption .esxc-unit { + font-size: 18px; +} + +.esxc-gauge-large .esxc-gauge-circle-caption .esxc-loading { + font-size: 30px; +} + +.esxc-gauge-large .esxc-limit { + text-align: center; + margin-top: 4px; +} + +.esxc-gauge-large .esxc-limit .esxc-value { + font-size: 14px; + color: #565656; + line-height: 18px; +} + +.esxc-gauge-large .esxc-limit .esxc-unit { + font-size: 14px; +} + +.esxc-gauge-large .esxc-limit .esxc-label { + font-size: 14px; +} + +.esxc-gauge-large .esxc-limit .esxc-bar-limit { + text-align: right; +} + +.esxc-gauge-large .esxc-title { + font-size: 18px; + font-weight: 600; + color: #565656; + line-height: 18px; + text-align: center; + margin-top: -2px; +} + +.esxc-gauge-large .esxc-title .esxc-bar-title { + float: left; + text-align: right; + width: 70px; + margin-top: 0; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/gauge/gauge.component.html b/src/ui_ng/src/app/shared/gauge/gauge.component.html new file mode 100644 index 000000000..372bf8f41 --- /dev/null +++ b/src/ui_ng/src/app/shared/gauge/gauge.component.html @@ -0,0 +1,18 @@ +
+
+
+
+
+
+
+ {{used}} +
+
+
+
+ {{threasHold}} + GB + {{'STATISTICS.LIMIT' | translate}} +
+
{{title | translate}}
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/gauge/gauge.component.ts b/src/ui_ng/src/app/shared/gauge/gauge.component.ts new file mode 100644 index 000000000..126538051 --- /dev/null +++ b/src/ui_ng/src/app/shared/gauge/gauge.component.ts @@ -0,0 +1,292 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { + Component, + Input, + AfterViewInit, + ViewChild, + ElementRef +} from '@angular/core'; + +import * as $ from 'jquery'; + +const RESOURCE_COLOR_GREEN_NORMAL: string = '#5DB700'; +const RESOURCE_COLOR_ORANGE_NORMAL: string = '#FBBF00'; +const RESOURCE_COLOR_RED_NORMAL: string = '#EA400D'; +const RESOURCE_COLOR_GREY500: string = '#D7DEE2'; +const RESOURCE_COLOR_GREY600: string = '#C7D1D6'; + +/** + * Guage to visualize percent usage. + */ +@Component({ + selector: 'esxc-gauge', + templateUrl: 'gauge.component.html', + styleUrls: ['gauge.component.css'] +}) + +export class GaugeComponent implements AfterViewInit { + private _backgroundColor: string; + private _colorOne: string; + private _colorTwo: string; + private _size: string = "small"; //Support small, medium, large + private _title: string = "UNKNOWN"; //Lang key + private _free: number = 0; + private _threasHold: number = 0; + + /** + * Background color of the component. Default is white. + */ + @Input() + get backgroundColor() { + if (this._backgroundColor) { + return this._backgroundColor; + } + return '#FAFAFA'; + } + + set backgroundColor(value: string) { + this._backgroundColor = value; + } + + private _positionOne: number; + /** + * Keep these two properties + * Percentage of the total width for the first portion of the bar. + * Bar one is rendered above bar two, so bar two's position should always + * be greater than bar one if you want bar two to be visible. + */ + @Input() + get positionOne(): number { + return this._positionOne; + } + + set positionOne(value: number) { + this._positionOne = value; + this.setBars(); + } + + private _positionTwo: number; + /** + * Percentage of the total width for the second portion of the bar + */ + @Input() + get positionTwo(): number { + return this._positionTwo; + } + + set positionTwo(value: number) { + this._positionTwo = this._positionOne + value; + this.setBars(); + } + + private _animate: boolean; + /** + * Whether to animate transitions in the bars + */ + @Input() + get animate(): boolean { + return this._animate; + } + + set animate(value: boolean) { + if (typeof value !== 'undefined') { + this._animate = value; + } + this.setAnimate(); + } + + //Define the gauge size + @Input() + get size(): string { + return this._size; + } + + set size(sz: string) { + if (typeof sz !== 'undefined') { + if (sz === 'small' || sz === 'medium' || sz === 'large') { + this._size = sz; + return; + } + } + + this._size = "small"; + } + + get sizeClass(): string { + return "esxc-gauge-" + this._size; + } + + @Input() + get title(): string { + return this._title; + } + + set title(t: string) { + if (typeof t !== 'undefined') { + this._title = t; + } + } + + @Input() + get free(): number { + return this._free; + } + + set free(u: number) { + this._free = u; + this.determineColors(); + } + + get used(): number { + return this._threasHold - this._free; + } + + @Input() + get threasHold(): number { + return this._threasHold; + } + + set threasHold(th: number) { + this._threasHold = th; + this.determineColors(); + } + + ngAfterViewInit() { + this.determineColors(); + } + + @ViewChild('barOne') private barOne: ElementRef; + @ViewChild('barTwo') private barTwo: ElementRef; + + private determineColors() { + let percent: number = 0; + if (this._threasHold !== 0) { + let used: number = this._threasHold - this._free; + if (used < 0) { + used = 0; + } + percent = (used / this._threasHold) * 100; + } + + while (percent > 100) { + percent = percent - 100; + } + + if (percent <= 70) { + this._colorOne = RESOURCE_COLOR_GREEN_NORMAL; + } else if (percent > 70 && percent <= 90) { + this._colorOne = RESOURCE_COLOR_ORANGE_NORMAL; + } else if (percent > 90 && percent <= 100) { + this._colorOne = RESOURCE_COLOR_RED_NORMAL; + } else { + this._colorOne = RESOURCE_COLOR_GREY600; + } + + this._positionOne = percent; + this.setBars(); + this.setColors(); + this.setAnimate(); + } + + private setBars() { + if (!this.barOne || !this.barTwo) { + return; + } + + let barOne = $(this.barOne.nativeElement); + let barTwo = $(this.barTwo.nativeElement); + + if (!barOne || !barTwo) { + return; + } + + let posOne, posTwo; + + if (isNaN(this.positionOne)) { + posOne = posTwo = 0; + } else { + posOne = (this.positionOne / 100) * 180; + posTwo = (this.positionTwo / 100) * 180; + } + + barOne.css({ + '-webkit-transform': 'rotate(' + posOne + 'deg)', + '-moz-transform': 'rotate(' + posOne + 'deg)', + '-ms-transform': 'rotate(' + posOne + 'deg)', + '-o-transform': 'rotate(' + posOne + 'deg)', + 'transform': 'rotate(' + posOne + 'deg)' + }); + + barTwo.css({ + '-webkit-transform': 'rotate(' + posTwo + 'deg)', + '-moz-transform': 'rotate(' + posTwo + 'deg)', + '-ms-transform': 'rotate(' + posTwo + 'deg)', + '-o-transform': 'rotate(' + posTwo + 'deg)', + 'transform': 'rotate(' + posTwo + 'deg)' + }); + } + + private setColors() { + if (!this.barOne || !this.barTwo) { + return; + } + + let barOne = $(this.barOne.nativeElement); + let barTwo = $(this.barTwo.nativeElement); + + if (!barOne || !barTwo) { + return; + } + + barOne.css({ + 'background-color': this._colorOne + }); + + barTwo.css({ + 'background-color': this._colorTwo + }); + } + + private setAnimate() { + if (!this.barOne || !this.barTwo) { + return; + } + + let barOne = $(this.barOne.nativeElement); + let barTwo = $(this.barTwo.nativeElement); + + if (!barOne || !barTwo) { + return; + } + + let transition = 'transform 1s ease'; + let prefixes = ['webkit', 'moz', 'ms', 'o']; + let css = { + 'transition': transition + }; + + if (!this._animate) { + transition = 'none'; + }; + + for (let prefix of prefixes) { + css['-' + prefix + '-transition'] = transition; + } + + barOne.css(css); + barTwo.css(css); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.css b/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.css new file mode 100644 index 000000000..0d96e37ca --- /dev/null +++ b/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.css @@ -0,0 +1,9 @@ +.alert-text-blink { + color: red; + font-weight: bolder; +} + +.alert-btn-link { + padding: 0px !important; + min-width: 30px !important; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.html b/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.html new file mode 100644 index 000000000..668e5392d --- /dev/null +++ b/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.html @@ -0,0 +1,11 @@ + +
+ + {{errorMessage}} + +
+ + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.ts b/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.ts new file mode 100644 index 000000000..1dd2b8402 --- /dev/null +++ b/src/ui_ng/src/app/shared/inline-alert/inline-alert.component.ts @@ -0,0 +1,96 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; + +import { errorHandler } from '../shared.utils'; +import { Observable } from 'rxjs/Rx'; +import { Subscription } from "rxjs"; + +@Component({ + selector: 'inline-alert', + templateUrl: "inline-alert.component.html", + styleUrls: ['inline-alert.component.css'] +}) +export class InlineAlertComponent { + private inlineAlertType: string = 'alert-danger'; + private inlineAlertClosable: boolean = false; + private alertClose: boolean = true; + private displayedText: string = ""; + private showCancelAction: boolean = false; + private useAppLevelStyle: boolean = false; + private timer: Subscription = null; + private count: number = 0; + private blinking: boolean = false; + + @Output() confirmEvt = new EventEmitter(); + + constructor(private translate: TranslateService) { } + + public get errorMessage(): string { + return this.displayedText; + } + + //Show error message inline + public showInlineError(error: any): void { + this.displayedText = errorHandler(error); + if (this.displayedText) { + this.translate.get(this.displayedText).subscribe((res: string) => this.displayedText = res); + } + + this.inlineAlertType = 'alert-danger'; + this.showCancelAction = false; + this.inlineAlertClosable = true; + this.alertClose = false; + this.useAppLevelStyle = false; + } + + //Show confirmation info with action button + public showInlineConfirmation(warning: any): void { + this.displayedText = ""; + if (warning && warning.message) { + this.translate.get(warning.message).subscribe((res: string) => this.displayedText = res); + } + this.inlineAlertType = 'alert-warning'; + this.showCancelAction = true; + this.inlineAlertClosable = false; + this.alertClose = false; + this.useAppLevelStyle = false; + } + + //Show inline sccess info + public showInlineSuccess(info: any): void { + this.displayedText = ""; + if (info && info.message) { + this.translate.get(info.message).subscribe((res: string) => this.displayedText = res); + } + this.inlineAlertType = 'alert-success'; + this.showCancelAction = false; + this.inlineAlertClosable = true; + this.alertClose = false; + this.useAppLevelStyle = false; + } + + //Close alert + public close(): void { + this.alertClose = true; + } + + public blink() { + } + + private confirmCancel(): void { + this.confirmEvt.emit(true); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/list-policy/list-policy.component.html b/src/ui_ng/src/app/shared/list-policy/list-policy.component.html new file mode 100644 index 000000000..0bb855249 --- /dev/null +++ b/src/ui_ng/src/app/shared/list-policy/list-policy.component.html @@ -0,0 +1,34 @@ + + {{'REPLICATION.NAME' | translate}} + {{'REPLICATION.PROJECT' | translate}} + {{'REPLICATION.DESCRIPTION' | translate}} + {{'REPLICATION.DESTINATION_NAME' | translate}} + {{'REPLICATION.LAST_START_TIME' | translate}} + {{'REPLICATION.ACTIVATION' | translate}} + + + + + + + + + + + {{p.project_name}} + {{p.description ? p.description : '-'}} + {{p.target_name}} + + + + + + {{ (p.enabled === 1 ? 'REPLICATION.ENABLED' : 'REPLICATION.DISABLED') | translate}} + + + {{ (policies ? policies.length : 0) }} {{'REPLICATION.ITEMS' | translate}} + \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/list-policy/list-policy.component.ts b/src/ui_ng/src/app/shared/list-policy/list-policy.component.ts new file mode 100644 index 000000000..eeffb464d --- /dev/null +++ b/src/ui_ng/src/app/shared/list-policy/list-policy.component.ts @@ -0,0 +1,146 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, Output, EventEmitter, ViewChild, OnDestroy } from '@angular/core'; + +import { ReplicationService } from '../../replication/replication.service'; +import { Policy } from '../../replication/policy'; + +import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service'; +import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message'; + +import { ConfirmationState, ConfirmationTargets } from '../../shared/shared.const'; + +import { MessageHandlerService } from '../../shared/message-handler/message-handler.service'; + +import { Subscription } from 'rxjs/Subscription'; + +@Component({ + selector: 'list-policy', + templateUrl: 'list-policy.component.html', +}) +export class ListPolicyComponent implements OnDestroy { + + nullTime: string = '0001-01-01T00:00:00Z'; + + @Input() policies: Policy[]; + @Input() projectless: boolean; + @Input() selectedId: number; + + @Output() reload = new EventEmitter(); + @Output() selectOne = new EventEmitter(); + @Output() editOne = new EventEmitter(); + @Output() toggleOne = new EventEmitter(); + + toggleSubscription: Subscription; + subscription: Subscription; + + constructor( + private replicationService: ReplicationService, + private toggleConfirmDialogService: ConfirmationDialogService, + private deletionDialogService: ConfirmationDialogService, + private messageHandlerService: MessageHandlerService) { + + this.toggleSubscription = this.toggleConfirmDialogService + .confirmationConfirm$ + .subscribe( + message=> { + if(message && + message.source === ConfirmationTargets.TOGGLE_CONFIRM && + message.state === ConfirmationState.CONFIRMED) { + let policy: Policy = message.data; + policy.enabled = policy.enabled === 0 ? 1 : 0; + console.log('Enable policy ID:' + policy.id + ' with activation status ' + policy.enabled); + this.replicationService + .enablePolicy(policy.id, policy.enabled) + .subscribe( + response => { + this.messageHandlerService.showSuccess('REPLICATION.TOGGLED_SUCCESS'); + console.log('Successful toggled policy status') + }, + error => this.messageHandlerService.handleError(error) + ); + } + } + ); + this.subscription = this.deletionDialogService + .confirmationConfirm$ + .subscribe( + message => { + if (message && + message.source === ConfirmationTargets.POLICY && + message.state === ConfirmationState.CONFIRMED) { + this.replicationService + .deletePolicy(message.data) + .subscribe( + response => { + this.messageHandlerService.showSuccess('REPLICATION.DELETED_SUCCESS'); + console.log('Successful delete policy with ID:' + message.data); + this.reload.emit(true); + }, + error => { + if(error && error.status === 412) { + this.messageHandlerService.handleError('REPLICATION.FAILED_TO_DELETE_POLICY_ENABLED'); + } else { + this.messageHandlerService.handleError(error); + } + } + ); + } + } + ); + + } + + ngOnDestroy() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + if(this.toggleSubscription) { + this.toggleSubscription.unsubscribe(); + } + } + + selectPolicy(policy: Policy): void { + this.selectedId = policy.id; + console.log('Select policy ID:' + policy.id); + this.selectOne.emit(policy); + } + + editPolicy(policy: Policy) { + console.log('Open modal to edit policy.'); + this.editOne.emit(policy); + } + + togglePolicy(policy: Policy) { + let toggleConfirmMessage: ConfirmationMessage = new ConfirmationMessage( + policy.enabled === 1 ? 'REPLICATION.TOGGLE_DISABLE_TITLE' : 'REPLICATION.TOGGLE_ENABLE_TITLE', + policy.enabled === 1 ? 'REPLICATION.CONFIRM_TOGGLE_DISABLE_POLICY': 'REPLICATION.CONFIRM_TOGGLE_ENABLE_POLICY', + policy.name, + policy, + ConfirmationTargets.TOGGLE_CONFIRM + ); + this.toggleConfirmDialogService.openComfirmDialog(toggleConfirmMessage); + } + + deletePolicy(policy: Policy) { + let deletionMessage: ConfirmationMessage = new ConfirmationMessage( + 'REPLICATION.DELETION_TITLE', + 'REPLICATION.DELETION_SUMMARY', + policy.name, + policy.id, + ConfirmationTargets.POLICY); + this.deletionDialogService.openComfirmDialog(deletionMessage); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/list-project-ro/list-project-ro.component.html b/src/ui_ng/src/app/shared/list-project-ro/list-project-ro.component.html new file mode 100644 index 000000000..591e39361 --- /dev/null +++ b/src/ui_ng/src/app/shared/list-project-ro/list-project-ro.component.html @@ -0,0 +1,16 @@ + + {{'PROJECT.NAME' | translate}} + {{'PROJECT.PUBLIC_OR_PRIVATE' | translate}} + {{'PROJECT.REPO_COUNT'| translate}} + {{'PROJECT.CREATION_TIME' | translate}} + + {{p.name}} + {{ (p.public === 1 ? 'PROJECT.PUBLIC' : 'PROJECT.PRIVATE') | translate}} + {{p.repo_count}} + {{p.creation_time | date: 'short'}} + + + {{totalRecordCount || (projects ? projects.length : 0)}} {{'PROJECT.ITEMS' | translate}} + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/list-project-ro/list-project-ro.component.ts b/src/ui_ng/src/app/shared/list-project-ro/list-project-ro.component.ts new file mode 100644 index 000000000..e0f4fac6e --- /dev/null +++ b/src/ui_ng/src/app/shared/list-project-ro/list-project-ro.component.ts @@ -0,0 +1,49 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, EventEmitter, Output, Input } from '@angular/core'; +import { Router } from '@angular/router'; + +import { SearchTriggerService } from '../../base/global-search/search-trigger.service'; + +import { Project } from '../../project/project'; +import { State } from 'clarity-angular'; + +@Component({ + selector: 'list-project-ro', + templateUrl: 'list-project-ro.component.html' +}) +export class ListProjectROComponent { + @Input() projects: Project[]; + + @Input() totalPage: number; + @Input() totalRecordCount: number; + pageOffset: number = 1; + + @Output() paginate = new EventEmitter(); + + constructor( + private searchTrigger: SearchTriggerService, + private router: Router) { } + + goToLink(proId: number): void { + this.searchTrigger.closeSearch(true); + + let linkUrl = ['harbor', 'projects', proId, 'repository']; + this.router.navigate(linkUrl); + } + + refresh(state: State) { + this.paginate.emit(state); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/list-repository-ro/list-repository-ro.component.html b/src/ui_ng/src/app/shared/list-repository-ro/list-repository-ro.component.html new file mode 100644 index 000000000..b9c878983 --- /dev/null +++ b/src/ui_ng/src/app/shared/list-repository-ro/list-repository-ro.component.html @@ -0,0 +1,14 @@ + + {{'REPOSITORY.NAME' | translate}} + {{'REPOSITORY.TAGS_COUNT' | translate}} + {{'REPOSITORY.PULL_COUNT' | translate}} + + {{r.name || r.repository_name}} + {{r.tags_count}} + {{r.pull_count}} + + + {{totalRecordCount || (repositories ? repositories.length : 0)}} {{'REPOSITORY.ITEMS' | translate}} + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/list-repository-ro/list-repository-ro.component.ts b/src/ui_ng/src/app/shared/list-repository-ro/list-repository-ro.component.ts new file mode 100644 index 000000000..733c5242d --- /dev/null +++ b/src/ui_ng/src/app/shared/list-repository-ro/list-repository-ro.component.ts @@ -0,0 +1,53 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Router, NavigationExtras } from '@angular/router'; +import { Repository } from '../../repository/repository'; +import { State } from 'clarity-angular'; + +import { SearchTriggerService } from '../../base/global-search/search-trigger.service'; + +@Component({ + selector: 'list-repository-ro', + templateUrl: 'list-repository-ro.component.html' +}) +export class ListRepositoryROComponent { + + @Input() projectId: number; + @Input() repositories: Repository[]; + + @Input() totalPage: number; + @Input() totalRecordCount: number; + @Output() paginate = new EventEmitter(); + pageOffset: number = 1; + + constructor( + private router: Router, + private searchTrigger: SearchTriggerService + ) { } + + refresh(state: State) { + if (this.repositories) { + this.paginate.emit(state); + } + } + + public gotoLink(projectId: number, repoName: string): void { + this.searchTrigger.closeSearch(true); + + let linkUrl = ['harbor', 'tags', projectId, repoName]; + this.router.navigate(linkUrl); + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/max-length-ext.directive.ts b/src/ui_ng/src/app/shared/max-length-ext.directive.ts new file mode 100644 index 000000000..ab5bbbb59 --- /dev/null +++ b/src/ui_ng/src/app/shared/max-length-ext.directive.ts @@ -0,0 +1,63 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Directive, OnChanges, Input, SimpleChanges } from '@angular/core'; +import { ValidatorFn, AbstractControl, Validator, NG_VALIDATORS, Validators } from '@angular/forms'; + +export const assiiChars = /[\u4e00-\u9fa5]/; + +export function maxLengthExtValidator(length: number): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const value: string = control.value + if (!value || value.trim() === "") { + return null; + } + + const regExp = new RegExp(assiiChars, 'i'); + let count = 0; + let len = value.length; + + for (var i = 0; i < len; i++) { + if (regExp.test(value[i])) { + count += 3; + } else { + count++; + } + } + return count > length ? { 'maxLengthExt': count } : null; + } +} + +@Directive({ + selector: '[maxLengthExt]', + providers: [{ provide: NG_VALIDATORS, useExisting: MaxLengthExtValidatorDirective, multi: true }] +}) + +export class MaxLengthExtValidatorDirective implements Validator, OnChanges { + @Input() maxLengthExt: number; + private valFn = Validators.nullValidator; + + ngOnChanges(changes: SimpleChanges): void { + const change = changes['maxLengthExt']; + if (change) { + const val: number = change.currentValue; + this.valFn = maxLengthExtValidator(val); + } else { + this.valFn = Validators.nullValidator; + } + } + + validate(control: AbstractControl): { [key: string]: any } { + return this.valFn(control); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/message-handler/message-handler.service.ts b/src/ui_ng/src/app/shared/message-handler/message-handler.service.ts new file mode 100644 index 000000000..253697c35 --- /dev/null +++ b/src/ui_ng/src/app/shared/message-handler/message-handler.service.ts @@ -0,0 +1,88 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core' +import { Subject } from 'rxjs/Subject'; + +import { MessageService } from '../../global-message/message.service'; +import { AlertType, httpStatusCode } from '../../shared/shared.const'; +import { errorHandler } from '../../shared/shared.utils'; +import { TranslateService } from '@ngx-translate/core'; +import { SessionService } from '../../shared/session.service'; + +@Injectable() +export class MessageHandlerService { + + constructor( + private msgService: MessageService, + private translate: TranslateService, + private session: SessionService) { } + + //Handle the error and map it to the suitable message + //base on the status code of error. + + public handleError(error: any | string): void { + if (!error) { + return; + } + let msg = errorHandler(error); + + if (!(error.statusCode || error.status)) { + this.msgService.announceMessage(500, msg, AlertType.DANGER); + } else { + let code = error.statusCode | error.status; + if (code === httpStatusCode.Unauthorized) { + this.msgService.announceAppLevelMessage(code, msg, AlertType.DANGER); + //Session is invalida now, clare session cache + this.session.clear(); + } else { + this.msgService.announceMessage(code, msg, AlertType.DANGER); + } + } + } + + public showError(message: string, params: any): void { + if (!params) { + params = {}; + } + this.translate.get(message, params).subscribe((res: string) => { + this.msgService.announceMessage(500, res, AlertType.DANGER); + }); + } + + public showSuccess(message: string): void { + if (message && message.trim() != "") { + this.msgService.announceMessage(200, message, AlertType.SUCCESS); + } + } + + public showInfo(message: string): void { + if (message && message.trim() != "") { + this.msgService.announceMessage(200, message, AlertType.INFO); + } + } + + public showWarning(message: string): void { + if (message && message.trim() != "") { + this.msgService.announceMessage(400, message, AlertType.WARNING); + } + } + + public clear(): void { + this.msgService.clear(); + } + + public isAppLevel(error): boolean { + return error && error.statusCode === httpStatusCode.Unauthorized; + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.css b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.css new file mode 100644 index 000000000..d8273fda2 --- /dev/null +++ b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.css @@ -0,0 +1,10 @@ +.label-info { + margin: 0px !important; + padding: 0px !important; + margin-top: -5px !important; +} + +.spinner-pos { + margin-right: 0px !important; + top: 2px; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.html b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.html new file mode 100644 index 000000000..5a6e6ecef --- /dev/null +++ b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.html @@ -0,0 +1,88 @@ +
+
+
+
+ + +
+
+ + + + +
+
+ + +
+
+ + + +
+
+ + +
+
+ + +
+
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.ts b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.ts new file mode 100644 index 000000000..414962863 --- /dev/null +++ b/src/ui_ng/src/app/shared/new-user-form/new-user-form.component.ts @@ -0,0 +1,222 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { + Component, + ViewChild, + AfterViewChecked, + Output, + EventEmitter, + Input, + OnInit +} from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { User } from '../../user/user'; +import { isEmptyForm } from '../../shared/shared.utils'; +import { SessionService } from '../../shared/session.service'; + +@Component({ + selector: 'new-user-form', + templateUrl: 'new-user-form.component.html', + styleUrls: ['new-user-form.component.css', '../../common.css'] +}) + +export class NewUserFormComponent implements AfterViewChecked, OnInit { + newUser: User = new User(); + @Input() isSelfRegistration: boolean = false; + + newUserFormRef: NgForm; + @ViewChild("newUserFrom") newUserForm: NgForm; + + //Notify the form value changes + @Output() valueChange = new EventEmitter(); + + constructor(private session: SessionService) { } + + ngOnInit() { + this.resetState(); + } + + private validationStateMap: any = {}; + + private mailAlreadyChecked: any = {}; + private userNameAlreadyChecked: any = {}; + private emailTooltip: string = 'TOOLTIP.EMAIL'; + private usernameTooltip: string = 'TOOLTIP.USER_NAME'; + private formValueChanged: boolean = false; + + private checkOnGoing: any = {}; + + private resetState(): void { + this.mailAlreadyChecked = {}; + this.userNameAlreadyChecked = {}; + this.emailTooltip = 'TOOLTIP.EMAIL'; + this.usernameTooltip = 'TOOLTIP.USER_NAME'; + this.formValueChanged = false; + this.checkOnGoing = { + "username": false, + "email": false + }; + this.validationStateMap = { + "username": true, + "email": true, + "realname": true, + "newPassword": true, + "confirmPassword": true, + "comment": true + }; + } + + public isChecking(key: string): boolean { + return !this.checkOnGoing[key]; + } + + private getValidationState(key: string): boolean { + return !this.validationStateMap[key]; + } + + private handleValidation(key: string, flag: boolean): void { + if (flag) { + //Checking + let cont = this.newUserForm.controls[key]; + if (cont) { + this.validationStateMap[key] = cont.valid; + //Check email existing from backend + if (cont.valid && this.formValueChanged) { + //Check username from backend + if (key === "username" && this.newUser.username.trim() != "") { + if (this.userNameAlreadyChecked[this.newUser.username.trim()]) { + this.validationStateMap[key] = !this.userNameAlreadyChecked[this.newUser.username.trim()].result; + if (!this.validationStateMap[key]) { + this.usernameTooltip = "TOOLTIP.USER_EXISTING"; + } + return; + } + + this.checkOnGoing[key] = true; + this.session.checkUserExisting("username", this.newUser.username) + .then((res: boolean) => { + this.checkOnGoing[key] = false; + this.validationStateMap[key] = !res; + if (res) { + this.usernameTooltip = "TOOLTIP.USER_EXISTING"; + } + this.userNameAlreadyChecked[this.newUser.username.trim()] = { + result: res + }; //Tag it checked + }) + .catch(error => { + this.checkOnGoing[key] = false; + this.validationStateMap[key] = false;//Not valid @ backend + }); + return; + + } + + //Check email from backend + if (key === "email" && this.newUser.email.trim() != "") { + if (this.mailAlreadyChecked[this.newUser.email.trim()]) { + this.validationStateMap[key] = !this.mailAlreadyChecked[this.newUser.email.trim()].result; + if (!this.validationStateMap[key]) { + this.emailTooltip = "TOOLTIP.EMAIL_EXISTING"; + } + return; + } + + //Mail changed + this.checkOnGoing[key] = true; + this.session.checkUserExisting("email", this.newUser.email) + .then((res: boolean) => { + this.checkOnGoing[key] = false; + this.validationStateMap[key] = !res; + if (res) { + this.emailTooltip = "TOOLTIP.EMAIL_EXISTING"; + } + this.mailAlreadyChecked[this.newUser.email.trim()] = { + result: res + }; //Tag it checked + }) + .catch(error => { + this.checkOnGoing[key] = false; + this.validationStateMap[key] = false;//Not valid @ backend + }); + return; + } + + //Check password confirmation + if (key === "confirmPassword" || key === "newPassword") { + let cpKey = key === "confirmPassword" ? "newPassword" : "confirmPassword"; + let peerCont = this.newUserForm.controls[cpKey]; + if (peerCont && peerCont.valid) { + this.validationStateMap["confirmPassword"] = cont.value === peerCont.value; + } + } + } + } + } else { + //Reset + this.validationStateMap[key] = true; + if (key === "email") { + this.emailTooltip = "TOOLTIP.EMAIL"; + } + + if (key === "username") { + this.usernameTooltip = "TOOLTIP.USER_NAME"; + } + } + } + + public get isValid(): boolean { + let pwdEqualStatus = true; + if (this.newUserForm.controls["confirmPassword"] && + this.newUserForm.controls["newPassword"]) { + pwdEqualStatus = this.newUserForm.controls["confirmPassword"].value === this.newUserForm.controls["newPassword"].value; + } + return this.newUserForm && + this.newUserForm.valid && + pwdEqualStatus && + this.validationStateMap["username"] && + this.validationStateMap["email"];//Backend check should be valid as well + } + + ngAfterViewChecked(): void { + if (this.newUserFormRef != this.newUserForm) { + this.newUserFormRef = this.newUserForm; + if (this.newUserFormRef) { + this.newUserFormRef.valueChanges.subscribe(data => { + this.formValueChanged = true; + this.valueChange.emit(true); + }); + } + } + } + + //Return the current user data + getData(): User { + return this.newUser; + } + + //Reset form + reset(): void { + this.resetState(); + if (this.newUserForm) { + this.newUserForm.reset(); + } + } + + //To check if form is empty + isEmpty(): boolean { + return isEmptyForm(this.newUserForm); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/not-found/not-found.component.css b/src/ui_ng/src/app/shared/not-found/not-found.component.css new file mode 100644 index 000000000..2c908a39c --- /dev/null +++ b/src/ui_ng/src/app/shared/not-found/not-found.component.css @@ -0,0 +1,32 @@ +.wrapper-back { + position: absolute; + top: 50%; + height: 240px; + margin-top: -120px; + text-align: center; + left: 50%; + margin-left: -300px; +} + +.status-code { + font-weight: bolder; + font-size: 4em; + vertical-align: middle; +} + +.status-text { + font-weight: bold; + font-size: 3em; + margin-left: 10px; + vertical-align: middle; +} + +.status-subtitle { + font-size: 18px; +} + +.second-number { + font-weight: bold; + font-size: 2em; + color: #EB8D00; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/not-found/not-found.component.html b/src/ui_ng/src/app/shared/not-found/not-found.component.html new file mode 100644 index 000000000..29e676128 --- /dev/null +++ b/src/ui_ng/src/app/shared/not-found/not-found.component.html @@ -0,0 +1,10 @@ +
+
+ + 404 + {{'PAGE_NOT_FOUND.MAIN_TITLE' | translate}} +
+
+ {{'PAGE_NOT_FOUND.SUB_TITLE' | translate}} {{leftSeconds}} {{'PAGE_NOT_FOUND.UNIT' | translate}} +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/not-found/not-found.component.ts b/src/ui_ng/src/app/shared/not-found/not-found.component.ts new file mode 100644 index 000000000..6f4df0797 --- /dev/null +++ b/src/ui_ng/src/app/shared/not-found/not-found.component.ts @@ -0,0 +1,48 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Router } from '@angular/router'; + +const defaultInterval = 1000; +const defaultLeftTime = 5; + +@Component({ + selector: 'page-not-found', + templateUrl: "not-found.component.html", + styleUrls: ['not-found.component.css'] +}) +export class PageNotFoundComponent implements OnInit, OnDestroy{ + private leftSeconds: number = defaultLeftTime; + private timeInterval: any = null; + + constructor(private router: Router){} + + ngOnInit(): void { + if(!this.timeInterval){ + this.timeInterval = setInterval(interval => { + this.leftSeconds--; + if(this.leftSeconds <= 0){ + this.router.navigate(['harbor']); + clearInterval(this.timeInterval); + } + }, defaultInterval); + } + } + + ngOnDestroy(): void { + if(this.timeInterval){ + clearInterval(this.timeInterval); + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/port.directive.ts b/src/ui_ng/src/app/shared/port.directive.ts new file mode 100644 index 000000000..65a351e25 --- /dev/null +++ b/src/ui_ng/src/app/shared/port.directive.ts @@ -0,0 +1,50 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Directive } from '@angular/core'; +import { ValidatorFn, AbstractControl, Validator, NG_VALIDATORS, Validators } from '@angular/forms'; + +export const portNumbers = /^[\d]{1,5}$/; + +export function portValidator(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } => { + const value: string = control.value + if (!value) { + return { 'port': 65535 }; + } + + const regExp = new RegExp(portNumbers, 'i'); + if(!regExp.test(value)){ + return { 'port': 65535 }; + }else{ + const portV = parseInt(value); + if(portV <=0 || portV >65535){ + return { 'port': 65535 }; + } + } + return null; + } +} + +@Directive({ + selector: '[port]', + providers: [{ provide: NG_VALIDATORS, useExisting: PortValidatorDirective, multi: true }] +}) + +export class PortValidatorDirective implements Validator { + private valFn = portValidator(); + + validate(control: AbstractControl): { [key: string]: any } { + return this.valFn(control); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/route/auth-user-activate.service.ts b/src/ui_ng/src/app/shared/route/auth-user-activate.service.ts new file mode 100644 index 000000000..9f5bf143d --- /dev/null +++ b/src/ui_ng/src/app/shared/route/auth-user-activate.service.ts @@ -0,0 +1,100 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { + CanActivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot, + CanActivateChild, + NavigationExtras +} from '@angular/router'; +import { SessionService } from '../../shared/session.service'; +import { CommonRoutes, AdmiralQueryParamKey } from '../../shared/shared.const'; +import { AppConfigService } from '../../app-config.service'; +import { maintainUrlQueryParmas } from '../../shared/shared.utils'; +import { MessageHandlerService } from '../message-handler/message-handler.service'; +import { SearchTriggerService } from '../../base/global-search/search-trigger.service'; + +@Injectable() +export class AuthCheckGuard implements CanActivate, CanActivateChild { + constructor( + private authService: SessionService, + private router: Router, + private appConfigService: AppConfigService, + private msgHandler: MessageHandlerService, + private searchTrigger: SearchTriggerService) { } + + private isGuest(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + const proRegExp = /\/harbor\/projects\/[\d]+\/.+/i; + const libRegExp = /\/harbor\/tags\/[\d]+\/.+/i; + if (proRegExp.test(state.url) || libRegExp.test(state.url)) { + return true; + } + + return false; + } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + //When routing change, clear + this.msgHandler.clear(); + this.searchTrigger.closeSearch(true); + return new Promise((resolve, reject) => { + //Before activating, we firstly need to confirm whether the route is coming from peer part - admiral + let queryParams = route.queryParams; + if (queryParams) { + if (queryParams[AdmiralQueryParamKey]) { + this.appConfigService.saveAdmiralEndpoint(queryParams[AdmiralQueryParamKey]); + //Remove the query parameter key pair and redirect + let keyRemovedUrl = maintainUrlQueryParmas(state.url, AdmiralQueryParamKey, undefined); + if (!/[?]{1}.+/i.test(keyRemovedUrl)) { + keyRemovedUrl = keyRemovedUrl.replace('?', ''); + } + + this.router.navigateByUrl(keyRemovedUrl); + return resolve(false); + } + } + + let user = this.authService.getCurrentUser(); + if (!user) { + this.authService.retrieveUser() + .then(() => resolve(true)) + .catch(error => { + //If is guest, skip it + if (this.isGuest(route, state)) { + return resolve(true); + } + //Session retrieving failed then redirect to sign-in + //no matter what status code is. + //Please pay attention that route 'HARBOR_ROOT' and 'EMBEDDED_SIGN_IN' support anonymous user + if (state.url != CommonRoutes.HARBOR_ROOT && !state.url.startsWith(CommonRoutes.EMBEDDED_SIGN_IN)) { + let navigatorExtra: NavigationExtras = { + queryParams: { "redirect_url": state.url } + }; + this.router.navigate([CommonRoutes.EMBEDDED_SIGN_IN], navigatorExtra); + return resolve(false); + } else { + return resolve(true); + } + }); + } else { + return resolve(true); + } + }); + } + + canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + return this.canActivate(route, state); + } +} diff --git a/src/ui_ng/src/app/shared/route/leaving-config-deactivate.service.ts b/src/ui_ng/src/app/shared/route/leaving-config-deactivate.service.ts new file mode 100644 index 000000000..4be35281f --- /dev/null +++ b/src/ui_ng/src/app/shared/route/leaving-config-deactivate.service.ts @@ -0,0 +1,64 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { + CanDeactivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot +} from '@angular/router'; + +import { ConfirmationDialogService } from '../confirmation-dialog/confirmation-dialog.service'; + +import { ConfigurationComponent } from '../../config/config.component'; +import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message'; +import { ConfirmationState, ConfirmationTargets } from '../shared.const'; + +@Injectable() +export class LeavingConfigRouteDeactivate implements CanDeactivate { + constructor( + private router: Router, + private confirmation: ConfirmationDialogService) { } + + canDeactivate( + config: ConfigurationComponent, + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Promise | boolean { + //Confirmation before leaving config route + return new Promise((resolve, reject) => { + if (config && config.hasChanges()) { + let msg: ConfirmationMessage = new ConfirmationMessage( + "CONFIG.LEAVING_CONFIRMATION_TITLE", + "CONFIG.LEAVING_CONFIRMATION_SUMMARY", + '', + {}, + ConfirmationTargets.CONFIG_ROUTE + ); + this.confirmation.openComfirmDialog(msg); + return this.confirmation.confirmationConfirm$.subscribe(msg => { + if (msg && msg.source === ConfirmationTargets.CONFIG_ROUTE) { + if (msg.state === ConfirmationState.CONFIRMED) { + return resolve(true); + } else { + return resolve(false);//Prevent leading route + } + } else { + return resolve(true);//Should go on + } + }); + } else { + return resolve(true); + } + }); + } +} diff --git a/src/ui_ng/src/app/shared/route/member-guard-activate.service.ts b/src/ui_ng/src/app/shared/route/member-guard-activate.service.ts new file mode 100644 index 000000000..ebf96d593 --- /dev/null +++ b/src/ui_ng/src/app/shared/route/member-guard-activate.service.ts @@ -0,0 +1,56 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { + CanActivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot, + CanActivateChild +} from '@angular/router'; +import { SessionService } from '../../shared/session.service'; +import { ProjectService } from '../../project/project.service'; +import { CommonRoutes } from '../../shared/shared.const'; + +@Injectable() +export class MemberGuard implements CanActivate, CanActivateChild { + constructor( + private sessionService: SessionService, + private projectService: ProjectService, + private router: Router) {} + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + let projectId = route.params['id']; + this.sessionService.setProjectMembers([]); + return new Promise((resolve, reject) => { + this.projectService.checkProjectMember(projectId) + .subscribe( + res=>{ + this.sessionService.setProjectMembers(res); + return resolve(true) + }, + error => { + //Add exception for repository in project detail router activation. + if(state.url.endsWith('repository')) { + return resolve(true); + } + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + return resolve(false); + }); + }); + } + + canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + return this.canActivate(route, state); + } +} diff --git a/src/ui_ng/src/app/shared/route/mode-guard-activate.service.ts b/src/ui_ng/src/app/shared/route/mode-guard-activate.service.ts new file mode 100644 index 000000000..ac679fd3e --- /dev/null +++ b/src/ui_ng/src/app/shared/route/mode-guard-activate.service.ts @@ -0,0 +1,54 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { + CanActivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot, + CanActivateChild +} from '@angular/router'; +import { CommonRoutes } from '../../shared/shared.const'; +import { AppConfigService } from '../../app-config.service'; + +@Injectable() +export class ModeGuard implements CanActivate, CanActivateChild { + constructor( + private router: Router, + private appConfigService: AppConfigService) { } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + //Show the right sign-in page for different modes + return new Promise((resolve, reject) => { + if (this.appConfigService.isIntegrationMode()) { + if (state.url.startsWith(CommonRoutes.SIGN_IN)) { + this.router.navigate([CommonRoutes.EMBEDDED_SIGN_IN], route.queryParams); + resolve(false); + } else { + resolve(true); + } + } else { + if (state.url.startsWith(CommonRoutes.EMBEDDED_SIGN_IN)) { + this.router.navigate([CommonRoutes.SIGN_IN], route.queryParams); + resolve(false); + } else { + resolve(true); + } + } + }); + } + + canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + return this.canActivate(route, state); + } +} diff --git a/src/ui_ng/src/app/shared/route/sign-in-guard-activate.service.ts b/src/ui_ng/src/app/shared/route/sign-in-guard-activate.service.ts new file mode 100644 index 000000000..df7621737 --- /dev/null +++ b/src/ui_ng/src/app/shared/route/sign-in-guard-activate.service.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { + CanActivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot, + CanActivateChild +} from '@angular/router'; +import { SessionService } from '../../shared/session.service'; +import { CommonRoutes } from '../../shared/shared.const'; +//import * as $ from 'jquery'; + +@Injectable() +export class SignInGuard implements CanActivate, CanActivateChild { + constructor(private authService: SessionService, private router: Router) { } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + //Fix overflow issue + /*let body = $(document.body); + if(body){ + body.css({ + "overflow-y": "hidden" + }); + }*/ + //If user has logged in, should not login again + return new Promise((resolve, reject) => { + //If signout appended + let queryParams = route.queryParams; + if (queryParams && queryParams['signout']) { + this.authService.signOff() + .then(() => { + this.authService.clear();//Destroy session cache + return resolve(true); + }) + .catch(error => { + console.error(error); + return resolve(false); + }); + } else { + let user = this.authService.getCurrentUser(); + if (user === null) { + this.authService.retrieveUser() + .then(() => { + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + return resolve(false); + }) + .catch(error => { + return resolve(true); + }); + } else { + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + return resolve(false); + } + } + }); + } + + canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + return this.canActivate(route, state); + } +} diff --git a/src/ui_ng/src/app/shared/route/start-guard-activate.service.ts b/src/ui_ng/src/app/shared/route/start-guard-activate.service.ts new file mode 100644 index 000000000..be22a4eaf --- /dev/null +++ b/src/ui_ng/src/app/shared/route/start-guard-activate.service.ts @@ -0,0 +1,51 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { + CanActivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot, + CanActivateChild +} from '@angular/router'; +import { SessionService } from '../../shared/session.service'; +import { CommonRoutes } from '../../shared/shared.const'; + +@Injectable() +export class StartGuard implements CanActivate, CanActivateChild { + constructor(private authService: SessionService, private router: Router) { } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + //Authentidacted user should not see the start page now + return new Promise((resolve, reject) => { + let user = this.authService.getCurrentUser(); + if (!user) { + this.authService.retrieveUser() + .then(() => { + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + return resolve(false); + }) + .catch(error => { + return resolve(true); + }); + } else { + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + return resolve(false); + } + }); + } + + canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + return this.canActivate(route, state); + } +} diff --git a/src/ui_ng/src/app/shared/route/system-admin-activate.service.ts b/src/ui_ng/src/app/shared/route/system-admin-activate.service.ts new file mode 100644 index 000000000..5b1e9300b --- /dev/null +++ b/src/ui_ng/src/app/shared/route/system-admin-activate.service.ts @@ -0,0 +1,76 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { + CanActivate, Router, + ActivatedRouteSnapshot, + RouterStateSnapshot, + CanActivateChild, + NavigationExtras +} from '@angular/router'; +import { SessionService } from '../../shared/session.service'; +import { CommonRoutes } from '../../shared/shared.const'; +import { AppConfigService } from '../../app-config.service'; + +@Injectable() +export class SystemAdminGuard implements CanActivate, CanActivateChild { + constructor( + private authService: SessionService, + private router: Router, + private appConfigService: AppConfigService) { } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + return new Promise((resolve, reject) => { + let user = this.authService.getCurrentUser(); + if (!user) { + this.authService.retrieveUser() + .then(() => { + //updated user + user = this.authService.getCurrentUser(); + if (user.has_admin_role > 0) { + return resolve(true); + } else { + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + return resolve(false); + } + }) + .catch(error => { + //Session retrieving failed then redirect to sign-in + //no matter what status code is. + //Please pay attention that route 'harborRootRoute' support anonymous user + if (state.url != CommonRoutes.HARBOR_ROOT && !state.url.startsWith(CommonRoutes.EMBEDDED_SIGN_IN)) { + let navigatorExtra: NavigationExtras = { + queryParams: { "redirect_url": state.url } + }; + this.router.navigate([CommonRoutes.EMBEDDED_SIGN_IN], navigatorExtra); + return resolve(false); + } else { + return resolve(true); + } + }); + } else { + if (user.has_admin_role > 0) { + return resolve(true); + } else { + this.router.navigate([CommonRoutes.HARBOR_DEFAULT]); + return resolve(false); + } + } + }); + } + + canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise | boolean { + return this.canActivate(route, state); + } +} diff --git a/src/ui_ng/src/app/shared/session-user.ts b/src/ui_ng/src/app/shared/session-user.ts new file mode 100644 index 000000000..9925eba06 --- /dev/null +++ b/src/ui_ng/src/app/shared/session-user.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//Define the session user +export class SessionUser { + user_id: number; + username: string; + email: string; + realname: string; + role_name?: string; + role_id?: number; + has_admin_role?: number; + comment: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/session.service.ts b/src/ui_ng/src/app/shared/session.service.ts new file mode 100644 index 000000000..75490bbe7 --- /dev/null +++ b/src/ui_ng/src/app/shared/session.service.ts @@ -0,0 +1,178 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, URLSearchParams } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { SessionUser } from './session-user'; +import { Member } from '../project/member/member'; + +import { SignInCredential } from './sign-in-credential'; +import { enLang } from '../shared/shared.const' + +const signInUrl = '/login'; +const currentUserEndpint = "/api/users/current"; +const signOffEndpoint = "/log_out"; +const accountEndpoint = "/api/users/:id"; +const langEndpoint = "/language"; +const userExistsEndpoint = "/userExists"; +const langMap = { + "zh": "zh-CN", + "en": "en-US" +}; + +/** + * Define related methods to handle account and session corresponding things + * + * @export + * @class SessionService + */ +@Injectable() +export class SessionService { + currentUser: SessionUser = null; + + projectMembers: Member[]; + + private headers = new Headers({ + "Content-Type": 'application/json' + }); + + private formHeaders = new Headers({ + "Content-Type": 'application/x-www-form-urlencoded' + }); + + constructor(private http: Http) { } + + //Handle the related exceptions + private handleError(error: any): Promise { + return Promise.reject(error.message || error); + } + + //Clear session + clear(): void { + this.currentUser = null; + this.projectMembers = []; + } + + //Submit signin form to backend (NOT restful service) + signIn(signInCredential: SignInCredential): Promise { + //Build the form package + const body = new URLSearchParams(); + body.set('principal', signInCredential.principal); + body.set('password', signInCredential.password); + + //Trigger Http + return this.http.post(signInUrl, body.toString(), { headers: this.formHeaders }) + .toPromise() + .then(() => null) + .catch(error => this.handleError(error)); + } + + /** + * Get the related information of current signed in user from backend + * + * @returns {Promise} + * + * @memberOf SessionService + */ + retrieveUser(): Promise { + return this.http.get(currentUserEndpint, { headers: this.headers }).toPromise() + .then(response => this.currentUser = response.json() as SessionUser) + .catch(error => this.handleError(error)) + } + + /** + * For getting info + */ + getCurrentUser(): SessionUser { + return this.currentUser; + } + + /** + * Log out the system + */ + signOff(): Promise { + return this.http.get(signOffEndpoint, { headers: this.headers }).toPromise() + .then(() => { + //Destroy current session cache + //this.currentUser = null; + }) //Nothing returned + .catch(error => this.handleError(error)) + } + + /** + * + * Update accpunt settings + * + * @param {SessionUser} account + * @returns {Promise} + * + * @memberOf SessionService + */ + updateAccountSettings(account: SessionUser): Promise { + if (!account) { + return Promise.reject("Invalid account settings"); + } + let putUrl = accountEndpoint.replace(":id", account.user_id + ""); + return this.http.put(putUrl, JSON.stringify(account), { headers: this.headers }).toPromise() + .then(() => { + //Retrieve current session user + return this.retrieveUser(); + }) + .catch(error => this.handleError(error)) + } + + /** + * Switch the backend language profile + */ + switchLanguage(lang: string): Promise { + if (!lang) { + return Promise.reject("Invalid language"); + } + + let backendLang = langMap[lang]; + if (!backendLang) { + backendLang = langMap[enLang]; + } + + let getUrl = langEndpoint + "?lang=" + backendLang; + return this.http.get(getUrl).toPromise() + .then(() => null) + .catch(error => this.handleError(error)) + } + + checkUserExisting(target: string, value: string): Promise { + //Build the form package + const body = new URLSearchParams(); + body.set('target', target); + body.set('value', value); + + //Trigger Http + return this.http.post(userExistsEndpoint, body.toString(), { headers: this.formHeaders }) + .toPromise() + .then(response => { + return response.json(); + }) + .catch(error => this.handleError(error)); + } + + setProjectMembers(projectMembers: Member[]): void { + this.projectMembers = projectMembers; + } + + getProjectMembers(): Member[] { + return this.projectMembers; + } + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/shared.const.ts b/src/ui_ng/src/app/shared/shared.const.ts new file mode 100644 index 000000000..dc1b077a8 --- /dev/null +++ b/src/ui_ng/src/app/shared/shared.const.ts @@ -0,0 +1,72 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export const supportedLangs = ['en-us', 'zh-cn']; +export const enLang = "en-us"; +export const languageNames = { + "en-us": "English", + "zh-cn": "中文简体" +}; +export const enum AlertType { + DANGER, WARNING, INFO, SUCCESS +}; + +export const dismissInterval = 15 * 1000; +export const httpStatusCode = { + "Unauthorized": 401, + "Forbidden": 403 +}; +export const enum ConfirmationTargets { + EMPTY, + PROJECT, + PROJECT_MEMBER, + USER, + POLICY, + TOGGLE_CONFIRM, + TARGET, + REPOSITORY, + TAG, + CONFIG, + CONFIG_ROUTE, + CONFIG_TAB +}; + +export const enum ActionType { + ADD_NEW, EDIT +}; + +export const ListMode = { + READONLY: "readonly", + FULL: "full" +}; + +export const CommonRoutes = { + SIGN_IN: "/sign-in", + EMBEDDED_SIGN_IN: "/harbor/sign-in", + SIGN_UP: "/sign-in?sign_up=true", + EMBEDDED_SIGN_UP: "/harbor/sign-in?sign_up=true", + HARBOR_ROOT: "/harbor", + HARBOR_DEFAULT: "/harbor/projects" +}; + +export const AdmiralQueryParamKey = "admiral_redirect_url"; +export const HarborQueryParamKey = "harbor_redirect_url"; +export const CookieKeyOfAdmiral = "admiral.endpoint.latest"; + +export const enum ConfirmationState { + NA, CONFIRMED, CANCEL +} + +export const ProjectTypes = { 0: 'PROJECT.MY_PROJECTS', 1: 'PROJECT.PUBLIC_PROJECTS' }; +export const RoleInfo = { 1: 'MEMBER.PROJECT_ADMIN', 2: 'MEMBER.DEVELOPER', 3: 'MEMBER.GUEST' }; +export const RoleMapping = { 'projectAdmin': 'MEMBER.PROJECT_ADMIN', 'developer': 'MEMBER.DEVELOPER', 'guest': 'MEMBER.GUEST' }; diff --git a/src/ui_ng/src/app/shared/shared.module.ts b/src/ui_ng/src/app/shared/shared.module.ts new file mode 100644 index 000000000..e336ca191 --- /dev/null +++ b/src/ui_ng/src/app/shared/shared.module.ts @@ -0,0 +1,120 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { CoreModule } from '../core/core.module'; +import { CookieService } from 'angular2-cookie/core'; + +import { SessionService } from '../shared/session.service'; +import { MessageComponent } from '../global-message/message.component'; + +import { MessageService } from '../global-message/message.service'; +import { MaxLengthExtValidatorDirective } from './max-length-ext.directive'; +import { FilterComponent } from './filter/filter.component'; +import { TranslateModule } from "@ngx-translate/core"; + +import { RouterModule } from '@angular/router'; + +import { ConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component'; +import { ConfirmationDialogService } from './confirmation-dialog/confirmation-dialog.service'; +import { SystemAdminGuard } from './route/system-admin-activate.service'; +import { NewUserFormComponent } from './new-user-form/new-user-form.component'; +import { InlineAlertComponent } from './inline-alert/inline-alert.component'; + +import { ListPolicyComponent } from './list-policy/list-policy.component'; +import { CreateEditPolicyComponent } from './create-edit-policy/create-edit-policy.component'; + +import { PortValidatorDirective } from './port.directive'; + +import { PageNotFoundComponent } from './not-found/not-found.component'; +import { AboutDialogComponent } from './about-dialog/about-dialog.component'; + +import { AuthCheckGuard } from './route/auth-user-activate.service'; + +import { StatisticsComponent } from './statictics/statistics.component'; +import { StatisticsPanelComponent } from './statictics/statistics-panel.component'; +import { SignInGuard } from './route/sign-in-guard-activate.service'; +import { LeavingConfigRouteDeactivate } from './route/leaving-config-deactivate.service'; +import { MemberGuard } from './route/member-guard-activate.service'; + +import { ListProjectROComponent } from './list-project-ro/list-project-ro.component'; +import { ListRepositoryROComponent } from './list-repository-ro/list-repository-ro.component'; + +import { MessageHandlerService } from './message-handler/message-handler.service'; +import { EmailValidatorDirective } from './email.directive'; +import { GaugeComponent } from './gauge/gauge.component'; +import { StatisticHandler } from './statictics/statistic-handler.service'; + +@NgModule({ + imports: [ + CoreModule, + TranslateModule, + RouterModule + ], + declarations: [ + MessageComponent, + MaxLengthExtValidatorDirective, + FilterComponent, + ConfirmationDialogComponent, + NewUserFormComponent, + InlineAlertComponent, + ListPolicyComponent, + CreateEditPolicyComponent, + PortValidatorDirective, + PageNotFoundComponent, + AboutDialogComponent, + StatisticsComponent, + StatisticsPanelComponent, + ListProjectROComponent, + ListRepositoryROComponent, + EmailValidatorDirective, + GaugeComponent + ], + exports: [ + CoreModule, + MessageComponent, + MaxLengthExtValidatorDirective, + FilterComponent, + TranslateModule, + ConfirmationDialogComponent, + NewUserFormComponent, + InlineAlertComponent, + ListPolicyComponent, + CreateEditPolicyComponent, + PortValidatorDirective, + PageNotFoundComponent, + AboutDialogComponent, + StatisticsComponent, + StatisticsPanelComponent, + ListProjectROComponent, + ListRepositoryROComponent, + EmailValidatorDirective, + GaugeComponent + ], + providers: [ + SessionService, + MessageService, + CookieService, + ConfirmationDialogService, + SystemAdminGuard, + AuthCheckGuard, + SignInGuard, + LeavingConfigRouteDeactivate, + MemberGuard, + MessageHandlerService, + StatisticHandler + ] +}) +export class SharedModule { + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/shared.utils.ts b/src/ui_ng/src/app/shared/shared.utils.ts new file mode 100644 index 000000000..fa64eef16 --- /dev/null +++ b/src/ui_ng/src/app/shared/shared.utils.ts @@ -0,0 +1,110 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgForm } from '@angular/forms'; +import { httpStatusCode, AlertType } from './shared.const'; +import { MessageService } from '../global-message/message.service'; +/** + * To handle the error message body + * + * @export + * @returns {string} + */ +export const errorHandler = function (error: any): string { + if (!error) { + return "UNKNOWN_ERROR"; + } + console.log(error); + if (!(error.statusCode || error.status)) { + //treat as string message + return '' + error; + } else { + switch (error.statusCode || error.status) { + case 400: + return "BAD_REQUEST_ERROR"; + case 401: + return "UNAUTHORIZED_ERROR"; + case 403: + return "FORBIDDEN_ERROR"; + case 404: + return "NOT_FOUND_ERROR"; + case 412: + return "PRECONDITION_FAILED"; + case 409: + return "CONFLICT_ERROR"; + case 500: + return "SERVER_ERROR"; + default: + return "UNKNOWN_ERROR"; + } + } +} + +/** + * To check if form is empty + */ +export const isEmptyForm = function (ngForm: NgForm): boolean { + if (ngForm && ngForm.form) { + let values = ngForm.form.value; + if (values) { + for (var key in values) { + if (values[key]) { + return false; + } + } + } + + } + + return true; +} + +/** + * Hanlde the 401 and 403 code + * + * If handled the 401 or 403, then return true otherwise false + */ +export const accessErrorHandler = function (error: any, msgService: MessageService): boolean { + if (error && error.status && msgService) { + if (error.status === httpStatusCode.Unauthorized) { + msgService.announceAppLevelMessage(error.status, "UNAUTHORIZED_ERROR", AlertType.DANGER); + return true; + } + } + + return false; +} + +//Provide capability of reconstructing the query paramter +export const maintainUrlQueryParmas = function (uri: string, key: string, value: string): string { + let re: RegExp = new RegExp("([?&])" + key + "=.*?(&|#|$)", "i"); + if (value === undefined) { + if (uri.match(re)) { + return uri.replace(re, '$1$2'); + } else { + return uri; + } + } else { + if (uri.match(re)) { + return uri.replace(re, '$1' + key + "=" + value + '$2'); + } else { + var hash = ''; + if (uri.indexOf('#') !== -1) { + hash = uri.replace(/.*#/, '#'); + uri = uri.replace(/#.*/, ''); + } + var separator = uri.indexOf('?') !== -1 ? "&" : "?"; + return uri + separator + key + "=" + value + hash; + } + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/sign-in-credential.ts b/src/ui_ng/src/app/shared/sign-in-credential.ts new file mode 100644 index 000000000..a53a3f0aa --- /dev/null +++ b/src/ui_ng/src/app/shared/sign-in-credential.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * Declare class for store the sign in data, + * two prperties: + * principal: The username used to sign in + * password: The password used to sign in + * + * @export + * @class SignInCredential + */ + +export class SignInCredential { + principal: string; + password: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistic-handler.service.ts b/src/ui_ng/src/app/shared/statictics/statistic-handler.service.ts new file mode 100644 index 000000000..10838ddc1 --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistic-handler.service.ts @@ -0,0 +1,27 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; + +@Injectable() +export class StatisticHandler { + + private refreshSource = new Subject(); + + refreshChan$ = this.refreshSource.asObservable(); + + refresh() { + this.refreshSource.next(true); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics-panel.component.html b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.html new file mode 100644 index 000000000..61602f536 --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.html @@ -0,0 +1,44 @@ +
+
+
+
+
+
+ {{'STATISTICS.PRO_ITEM' | translate }} +
+
+ {{'STATISTICS.REPO_ITEM' | translate }} +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics-panel.component.ts b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.ts new file mode 100644 index 000000000..2c6a1b7d3 --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistics-panel.component.ts @@ -0,0 +1,103 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs/Subscription'; + +import { StatisticsService } from './statistics.service'; +import { Statistics } from './statistics'; + +import { SessionService } from '../session.service'; +import { Volumes } from './volumes'; + +import { MessageHandlerService } from '../message-handler/message-handler.service'; +import { StatisticHandler } from './statistic-handler.service'; + +@Component({ + selector: 'statistics-panel', + templateUrl: "statistics-panel.component.html", + styleUrls: ['statistics.component.css'], + providers: [StatisticsService] +}) + +export class StatisticsPanelComponent implements OnInit, OnDestroy { + + private originalCopy: Statistics = new Statistics(); + private volumesInfo: Volumes = new Volumes(); + refreshSub: Subscription; + + constructor( + private statistics: StatisticsService, + private msgHandler: MessageHandlerService, + private session: SessionService, + private statisticHandler: StatisticHandler) { + } + + ngOnInit(): void { + //Refresh + this.refreshSub = this.statisticHandler.refreshChan$.subscribe(clear => { + this.getStatistics(); + }); + + if (this.session.getCurrentUser()) { + this.getStatistics(); + } + + if (this.isValidSession) { + this.getVolumes(); + } + } + + ngOnDestroy() { + if (this.refreshSub) { + this.refreshSub.unsubscribe(); + } + } + + public get totalStorage(): number { + return this.getGBFromBytes(this.volumesInfo.storage.total); + } + + public get freeStorage(): number { + return this.getGBFromBytes(this.volumesInfo.storage.free); + } + + public getStatistics(): void { + this.statistics.getStatistics() + .then(statistics => this.originalCopy = statistics) + .catch(error => { + this.msgHandler.handleError(error); + }); + } + + public getVolumes(): void { + this.statistics.getVolumes() + .then(volumes => this.volumesInfo = volumes) + .catch(error => { + this.msgHandler.handleError(error); + }); + } + + public get isValidSession(): boolean { + let user = this.session.getCurrentUser(); + return user && user.has_admin_role > 0; + } + + public get isValidStorage(): boolean { + return this.volumesInfo.storage.total != 0; + } + + private getGBFromBytes(bytes: number): number { + return Math.round((bytes / (1024 * 1024 * 1024))); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics.component.css b/src/ui_ng/src/app/shared/statictics/statistics.component.css new file mode 100644 index 000000000..6b03edac3 --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistics.component.css @@ -0,0 +1,58 @@ +.statistic-wrapper { + padding: 4px; + margin: 4px; + text-align: right; + vertical-align: middle; + height: 30px; + display: inline-block; +} + +.statistic-data { + font-size: 16px; + font-weight: 600; + font-family: Metropolis, "Avenir Next", "Helvetica Neue", Arial, sans-serif; + line-height: 16px; +} + +.statistic-text { + font-size: 10px; + line-height: 10px; + text-transform: uppercase; + font-family: Metropolis, "Avenir Next", "Helvetica Neue", Arial, sans-serif; +} + +.statistic-column-block { + display: inline-block; + text-align: right; +} + +.statistic-column-title { + position: relative; + text-transform: uppercase; + font-size: 14px; + font-family: Metropolis, "Avenir Next", "Helvetica Neue", Arial, sans-serif; +} + +.statistic-column-title-pro { + top: -10px; +} + +.statistic-column-title-repo { + top: 3px; +} + +.statistic-item-divider { + height: 54px; + display: inline-block; + width: 1px; + background-color: #ccc; + opacity: 0.55; + margin-left: 4px; + margin-right: 12px; + position: relative; + top: 3px; +} + +.statistic-block { + display: inline-block; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics.component.html b/src/ui_ng/src/app/shared/statictics/statistics.component.html new file mode 100644 index 000000000..2d98c617e --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistics.component.html @@ -0,0 +1,4 @@ +
+ {{data}} + {{label}} +
\ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics.component.ts b/src/ui_ng/src/app/shared/statictics/statistics.component.ts new file mode 100644 index 000000000..0510b418f --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistics.component.ts @@ -0,0 +1,25 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'statistics', + templateUrl: "statistics.component.html", + styleUrls: ['statistics.component.css'] +}) + +export class StatisticsComponent { + @Input() label: string; + @Input() data: number = 0; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics.service.ts b/src/ui_ng/src/app/shared/statictics/statistics.service.ts new file mode 100644 index 000000000..25f96f078 --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistics.service.ts @@ -0,0 +1,52 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { Statistics } from './statistics'; +import { Volumes } from './volumes'; + +const statisticsEndpoint = "/api/statistics"; +const volumesEndpoint = "/api/systeminfo/volumes"; +/** + * Declare service to handle the top repositories + * + * + * @export + * @class GlobalSearchService + */ +@Injectable() +export class StatisticsService { + private headers = new Headers({ + "Content-Type": 'application/json' + }); + private options = new RequestOptions({ + headers: this.headers + }); + + constructor(private http: Http) { } + + getStatistics(): Promise { + return this.http.get(statisticsEndpoint, this.options).toPromise() + .then(response => response.json() as Statistics) + .catch(error => Promise.reject(error)); + } + + getVolumes(): Promise { + return this.http.get(volumesEndpoint, this.options).toPromise() + .then(response => response.json() as Volumes) + .catch(error => Promise.reject(error)); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/statistics.ts b/src/ui_ng/src/app/shared/statictics/statistics.ts new file mode 100644 index 000000000..290934af7 --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/statistics.ts @@ -0,0 +1,23 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export class Statistics { + constructor() {} + + my_project_count: number; + my_repo_count: number; + public_project_count: number; + public_repo_count: number; + total_project_count: number; + total_repo_count: number; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/statictics/volumes.ts b/src/ui_ng/src/app/shared/statictics/volumes.ts new file mode 100644 index 000000000..96edc5b88 --- /dev/null +++ b/src/ui_ng/src/app/shared/statictics/volumes.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export class Volumes { + constructor(){ + this.storage = new Storage(); + } + + storage: Storage; +} + +export class Storage { + constructor(){ + this.total = 0; + this.free = 0; + } + + total: number; + free: number; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/shared/target-exists-directive.ts b/src/ui_ng/src/app/shared/target-exists-directive.ts new file mode 100644 index 000000000..2adfb47ff --- /dev/null +++ b/src/ui_ng/src/app/shared/target-exists-directive.ts @@ -0,0 +1,79 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Directive, OnChanges, Input, SimpleChanges } from '@angular/core'; +import { NG_ASYNC_VALIDATORS, Validator, Validators, ValidatorFn, AbstractControl } from '@angular/forms'; + +import { ProjectService} from '../project/project.service'; + +import { MemberService } from '../project/member/member.service'; +import { Member } from '../project/member/member'; + +@Directive({ + selector: '[targetExists]', + providers: [ + ProjectService, MemberService, + { provide: NG_ASYNC_VALIDATORS, useExisting: TargetExistsValidatorDirective, multi: true}, + ] +}) +export class TargetExistsValidatorDirective implements Validator, OnChanges { + @Input() targetExists: string; + @Input() projectId: number; + + private valFn = Validators.nullValidator; + + constructor( + private projectService: ProjectService, + private memberService: MemberService) {} + + ngOnChanges(changes: SimpleChanges): void { + const change = changes['targetExists']; + if (change) { + const target: string = change.currentValue; + this.valFn = this.targetExistsValidator(target); + } else { + this.valFn = Validators.nullValidator; + } + } + validate(control: AbstractControl): {[key: string]: any} { + return this.valFn(control); + } + + targetExistsValidator(target: string): ValidatorFn { + return (control: AbstractControl): {[key: string]: any} => { + console.log('Target:' + target + ', validate value:' + control.value); + switch(target) { + case 'PROJECT_NAME': + return new Promise(resolve=>{ + this.projectService + .checkProjectExists(control.value) + .subscribe(res=>resolve({'targetExists': true}),error=>resolve(null)); + }); + case 'MEMBER_NAME': + return new Promise(resolve=>{ + this.memberService + .listMembers(this.projectId, control.value) + .subscribe((members: Member[])=>{ + return members.filter(m=>{ + if(m.username === control.value) { + return true; + } + return null; + }).length > 0 ? + resolve({'targetExists': true}) : resolve(null); + },error=>resolve(null)); + }); + } + } + } +} diff --git a/src/ui_ng/src/app/user/new-user-modal.component.html b/src/ui_ng/src/app/user/new-user-modal.component.html new file mode 100644 index 000000000..4725d73d8 --- /dev/null +++ b/src/ui_ng/src/app/user/new-user-modal.component.html @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/src/ui_ng/src/app/user/new-user-modal.component.ts b/src/ui_ng/src/app/user/new-user-modal.component.ts new file mode 100644 index 000000000..3071ac310 --- /dev/null +++ b/src/ui_ng/src/app/user/new-user-modal.component.ts @@ -0,0 +1,141 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, ViewChild, Output, EventEmitter } from '@angular/core'; +import { NgForm } from '@angular/forms'; + +import { NewUserFormComponent } from '../shared/new-user-form/new-user-form.component'; +import { User } from './user'; + +import { SessionService } from '../shared/session.service'; +import { UserService } from './user.service'; +import { InlineAlertComponent } from '../shared/inline-alert/inline-alert.component'; +import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; + +@Component({ + selector: "new-user-modal", + templateUrl: "new-user-modal.component.html" +}) + +export class NewUserModalComponent { + opened: boolean = false; + private error: any; + private onGoing: boolean = false; + private formValueChanged: boolean = false; + + @Output() addNew = new EventEmitter(); + + constructor(private session: SessionService, + private userService: UserService, + private msgHandler: MessageHandlerService) { } + + @ViewChild(NewUserFormComponent) + private newUserForm: NewUserFormComponent; + @ViewChild(InlineAlertComponent) + private inlineAlert: InlineAlertComponent; + + private getNewUser(): User { + return this.newUserForm.getData(); + } + + public get inProgress(): boolean { + return this.onGoing; + } + + public get isValid(): boolean { + return this.newUserForm.isValid && this.error == null; + } + + formValueChange(flag: boolean): void { + if (this.error != null) { + this.error = null;//clear error + } + + this.formValueChanged = true; + this.inlineAlert.close(); + } + + open(): void { + this.newUserForm.reset();//Reset form + this.formValueChanged = false; + this.onGoing = false; + this.error = null; + this.inlineAlert.close(); + + this.opened = true; + } + + close(): void { + if (this.formValueChanged) { + if (this.newUserForm.isEmpty()) { + this.opened = false; + } else { + //Need user confirmation + this.inlineAlert.showInlineConfirmation({ + message: "ALERT.FORM_CHANGE_CONFIRMATION" + }); + } + } else { + this.opened = false; + } + } + + confirmCancel(event: boolean): void { + this.opened = false; + } + + //Create new user + create(): void { + //Double confirm everything is ok + //Form is valid + if (!this.isValid) { + return; + } + + //We have new user data + let u = this.getNewUser(); + if (!u) { + return; + } + + //Session is ok and role is matched + let account = this.session.getCurrentUser(); + if (!account || account.has_admin_role === 0) { + return; + } + + //Start process + this.onGoing = true; + + this.userService.addUser(u) + .then(() => { + this.onGoing = false; + //TODO: + //As no response data returned, can not add it to list directly + + this.addNew.emit(u); + this.opened = false; + this.msgHandler.showSuccess("USER.SAVE_SUCCESS"); + }) + .catch(error => { + this.onGoing = false; + this.error = error; + if(this.msgHandler.isAppLevel(error)){ + this.msgHandler.handleError(error); + this.opened = false; + }else{ + this.inlineAlert.showInlineError(error); + } + }); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/user/user.component.css b/src/ui_ng/src/app/user/user.component.css new file mode 100644 index 000000000..cbd04a9ad --- /dev/null +++ b/src/ui_ng/src/app/user/user.component.css @@ -0,0 +1,37 @@ +.custom-h2 { + margin-top: 0px !important; +} + +.custom-add-button { + font-size: 12px; + margin-left: -12px; +} + +.filter-icon { + position: relative; + right: -12px; +} + +.filter-pos { + float: right; + margin-right: 24px; + position: relative; + top: 10px; +} + +.action-panel-pos { + position: relative; + padding-left: 12px; + margin-top: 12px; +} + +.refresh-btn { + position: absolute; + right: 2px; + top: 12px; + cursor: pointer; +} + +.hide-create { + visibility: hidden !important; +} \ No newline at end of file diff --git a/src/ui_ng/src/app/user/user.component.html b/src/ui_ng/src/app/user/user.component.html new file mode 100644 index 000000000..e8ed38f66 --- /dev/null +++ b/src/ui_ng/src/app/user/user.component.html @@ -0,0 +1,35 @@ +
+
+

{{'SIDE_NAV.SYSTEM_MGMT.USER' | translate}}

+
+ + + + + + + + +
+
+ + {{'USER.COLUMN_NAME' | translate}} + {{'USER.COLUMN_ADMIN' | translate}} + {{'USER.COLUMN_EMAIL' | translate}} + {{'USER.COLUMN_REG_NAME' | translate}} + + + + + + {{user.username}} + {{isSystemAdmin(user)}} + {{user.email}} + {{user.creation_time | date: 'short'}} + + {{users.length}} {{'USER.ITEMS' | translate}} + +
+ +
+
\ No newline at end of file diff --git a/src/ui_ng/src/app/user/user.component.ts b/src/ui_ng/src/app/user/user.component.ts new file mode 100644 index 000000000..cd90df25a --- /dev/null +++ b/src/ui_ng/src/app/user/user.component.ts @@ -0,0 +1,238 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; +import 'rxjs/add/operator/toPromise'; +import { Subscription } from 'rxjs/Subscription'; + +import { UserService } from './user.service'; +import { User } from './user'; +import { NewUserModalComponent } from './new-user-modal.component'; +import { TranslateService } from '@ngx-translate/core'; +import { ConfirmationDialogService } from '../shared/confirmation-dialog/confirmation-dialog.service'; +import { ConfirmationMessage } from '../shared/confirmation-dialog/confirmation-message'; +import { ConfirmationState, ConfirmationTargets } from '../shared/shared.const' +import { MessageHandlerService } from '../shared/message-handler/message-handler.service'; + +import { SessionService } from '../shared/session.service'; +import { AppConfigService } from '../app-config.service'; + +@Component({ + selector: 'harbor-user', + templateUrl: 'user.component.html', + styleUrls: ['user.component.css'], + + providers: [UserService] +}) + +export class UserComponent implements OnInit, OnDestroy { + users: User[] = []; + originalUsers: Promise; + private onGoing: boolean = false; + private adminMenuText: string = ""; + private adminColumn: string = ""; + private deletionSubscription: Subscription; + + currentTerm: string; + + @ViewChild(NewUserModalComponent) + private newUserDialog: NewUserModalComponent; + + constructor( + private userService: UserService, + private translate: TranslateService, + private deletionDialogService: ConfirmationDialogService, + private msgHandler: MessageHandlerService, + private session: SessionService, + private appConfigService: AppConfigService) { + this.deletionSubscription = deletionDialogService.confirmationConfirm$.subscribe(confirmed => { + if (confirmed && + confirmed.source === ConfirmationTargets.USER && + confirmed.state === ConfirmationState.CONFIRMED) { + this.delUser(confirmed.data); + } + }); + } + + private isMySelf(uid: number): boolean { + let currentUser = this.session.getCurrentUser(); + if (currentUser) { + if (currentUser.user_id === uid) { + return true; + } + } + + return false; + } + + private isMatchFilterTerm(terms: string, testedItem: string): boolean { + return testedItem.indexOf(terms) != -1; + } + + public get canCreateUser(): boolean { + let appConfig = this.appConfigService.getConfig(); + if (appConfig) { + return appConfig.auth_mode != 'ldap_auth'; + } else { + return true; + } + } + + isSystemAdmin(u: User): string { + if (!u) { + return "{{MISS}}"; + } + let key: string = u.has_admin_role ? "USER.IS_ADMIN" : "USER.IS_NOT_ADMIN"; + this.translate.get(key).subscribe((res: string) => this.adminColumn = res); + return this.adminColumn; + } + + adminActions(u: User): string { + if (!u) { + return "{{MISS}}"; + } + let key: string = u.has_admin_role ? "USER.DISABLE_ADMIN_ACTION" : "USER.ENABLE_ADMIN_ACTION"; + this.translate.get(key).subscribe((res: string) => this.adminMenuText = res); + return this.adminMenuText; + } + + public get inProgress(): boolean { + return this.onGoing; + } + + ngOnInit(): void { + this.refreshUser(); + } + + ngOnDestroy(): void { + if (this.deletionSubscription) { + this.deletionSubscription.unsubscribe(); + } + } + + //Filter items by keywords + doFilter(terms: string): void { + this.currentTerm = terms; + this.originalUsers.then(users => { + if (terms.trim() === "") { + this.users = users; + } else { + this.users = users.filter(user => { + return this.isMatchFilterTerm(terms, user.username); + }) + } + }); + } + + //Disable the admin role for the specified user + changeAdminRole(user: User): void { + //Double confirm user is existing + if (!user || user.user_id === 0) { + return; + } + + if (this.isMySelf(user.user_id)) { + return; + } + + //Value copy + let updatedUser: User = new User(); + updatedUser.user_id = user.user_id; + + if (user.has_admin_role === 0) { + updatedUser.has_admin_role = 1;//Set as admin + } else { + updatedUser.has_admin_role = 0;//Set as none admin + } + + this.userService.updateUserRole(updatedUser) + .then(() => { + //Change view now + user.has_admin_role = updatedUser.has_admin_role; + }) + .catch(error => { + this.msgHandler.handleError(error); + }) + } + + //Delete the specified user + deleteUser(user: User): void { + if (!user) { + return; + } + + if (this.isMySelf(user.user_id)) { + return; //Double confirm + } + + //Confirm deletion + let msg: ConfirmationMessage = new ConfirmationMessage( + "USER.DELETION_TITLE", + "USER.DELETION_SUMMARY", + user.username, + user, + ConfirmationTargets.USER + ); + this.deletionDialogService.openComfirmDialog(msg); + } + + private delUser(user: User): void { + this.userService.deleteUser(user.user_id) + .then(() => { + //Remove it from current user list + //and then view refreshed + this.currentTerm = ''; + this.originalUsers.then(users => { + this.users = users.filter(u => u.user_id != user.user_id); + this.msgHandler.showSuccess("USER.DELETE_SUCCESS"); + }); + }) + .catch(error => { + this.msgHandler.handleError(error); + }); + } + + //Refresh the user list + refreshUser(): void { + //Start to get + this.currentTerm = ''; + this.onGoing = true; + + this.originalUsers = this.userService.getUsers() + .then(users => { + this.onGoing = false; + + this.users = users; + return users; + }) + .catch(error => { + this.onGoing = false; + this.msgHandler.handleError(error); + }); + } + + //Add new user + addNewUser(): void { + if (!this.canCreateUser) { + return;// No response to this hacking action + } + this.newUserDialog.open(); + } + + //Add user to the user list + addUserToList(user: User): void { + //Currently we can only add it by reloading all + this.refreshUser(); + } + +} diff --git a/src/ui_ng/src/app/user/user.module.ts b/src/ui_ng/src/app/user/user.module.ts new file mode 100644 index 000000000..2041a8a63 --- /dev/null +++ b/src/ui_ng/src/app/user/user.module.ts @@ -0,0 +1,35 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { UserComponent } from './user.component'; +import { NewUserModalComponent } from './new-user-modal.component'; +import { UserService } from './user.service'; + +@NgModule({ + imports: [ + SharedModule + ], + declarations: [ + UserComponent, + NewUserModalComponent + ], + exports: [ + UserComponent + ], + providers:[UserService] +}) +export class UserModule { + +} \ No newline at end of file diff --git a/src/ui_ng/src/app/user/user.service.ts b/src/ui_ng/src/app/user/user.service.ts new file mode 100644 index 000000000..b12f9e172 --- /dev/null +++ b/src/ui_ng/src/app/user/user.service.ts @@ -0,0 +1,80 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Injectable } from '@angular/core'; +import { Headers, Http, RequestOptions } from '@angular/http'; +import 'rxjs/add/operator/toPromise'; + +import { User } from './user'; + +const userMgmtEndpoint = '/api/users'; + +/** + * Define related methods to handle account and session corresponding things + * + * @export + * @class SessionService + */ +@Injectable() +export class UserService { + private httpOptions = new RequestOptions({ + headers: new Headers({ + "Content-Type": 'application/json' + }) + }); + + constructor(private http: Http) { } + + //Handle the related exceptions + private handleError(error: any): Promise { + return Promise.reject(error.message || error); + } + + //Get the user list + getUsers(): Promise { + return this.http.get(userMgmtEndpoint, this.httpOptions).toPromise() + .then(response => response.json() as User[]) + .catch(error => this.handleError(error)); + } + + //Add new user + addUser(user: User): Promise { + return this.http.post(userMgmtEndpoint, JSON.stringify(user), this.httpOptions).toPromise() + .then(() => null) + .catch(error => this.handleError(error)); + } + + //Delete the specified user + deleteUser(userId: number): Promise { + return this.http.delete(userMgmtEndpoint + "/" + userId, this.httpOptions) + .toPromise() + .then(() => null) + .catch(error => this.handleError(error)); + } + + //Update user to enable/disable the admin role + updateUser(user: User): Promise { + return this.http.put(userMgmtEndpoint + "/" + user.user_id, JSON.stringify(user), this.httpOptions) + .toPromise() + .then(() => null) + .catch(error => this.handleError(error)); + } + + //Set user admin role + updateUserRole(user: User): Promise { + return this.http.put(userMgmtEndpoint + "/" + user.user_id + "/sysadmin", JSON.stringify(user), this.httpOptions) + .toPromise() + .then(() => null) + .catch(error => this.handleError(error)); + } +} \ No newline at end of file diff --git a/src/ui_ng/src/app/user/user.ts b/src/ui_ng/src/app/user/user.ts new file mode 100644 index 000000000..799303bd5 --- /dev/null +++ b/src/ui_ng/src/app/user/user.ts @@ -0,0 +1,29 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** + * For user management + * + * @export + * @class User + */ +export class User { + user_id: number; + username?: string; + realname?: string; + email?: string; + password?: string; + comment?: string; + has_admin_role?: number; + creation_time?: string; +} \ No newline at end of file diff --git a/src/ui_ng/src/environments/environment.prod.ts b/src/ui_ng/src/environments/environment.prod.ts new file mode 100644 index 000000000..6cc19b0bd --- /dev/null +++ b/src/ui_ng/src/environments/environment.prod.ts @@ -0,0 +1,16 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +export const environment = { + production: true +}; diff --git a/src/ui_ng/src/environments/environment.ts b/src/ui_ng/src/environments/environment.ts new file mode 100644 index 000000000..c685e380b --- /dev/null +++ b/src/ui_ng/src/environments/environment.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// The file contents for the current environment will overwrite these during build. +// The build system defaults to the dev environment which uses `environment.ts`, but if you do +// `ng build --env=prod` then `environment.prod.ts` will be used instead. +// The list of which env maps to which file can be found in `angular-cli.json`. + +export const environment = { + production: false +}; diff --git a/src/ui_ng/src/favicon.ico b/src/ui_ng/src/favicon.ico new file mode 100644 index 000000000..e6be46d4d Binary files /dev/null and b/src/ui_ng/src/favicon.ico differ diff --git a/src/ui_ng/src/i18n/lang/en-us-lang.json b/src/ui_ng/src/i18n/lang/en-us-lang.json new file mode 100644 index 000000000..96cc9d25f --- /dev/null +++ b/src/ui_ng/src/i18n/lang/en-us-lang.json @@ -0,0 +1,444 @@ +{ + "APP_TITLE": { + "VMW_HARBOR": "VMware Harbor", + "HARBOR": "Harbor", + "VIC": "vSphere Integrated Containers", + "MGMT": "Management", + "REG": "Registry" + }, + "SIGN_IN": { + "REMEMBER": "Remember me", + "INVALID_MSG": "Invalid user name or password.", + "FORGOT_PWD": "Forgot password", + "HEADER_LINK": "Sign In" + }, + "SIGN_UP": { + "TITLE": "Sign Up" + }, + "BUTTON": { + "CANCEL": "CANCEL", + "OK": "OK", + "DELETE": "DELETE", + "LOG_IN": "LOG IN", + "SIGN_UP_LINK": "Sign up for an account", + "SIGN_UP": "SIGN UP", + "CONFIRM": "CONFIRM", + "SEND": "SEND", + "SAVE": "SAVE", + "TEST_MAIL": "TEST MAIL SERVER", + "CLOSE": "CLOSE", + "TEST_LDAP": "TEST LDAP SERVER", + "MORE_INFO": "More info...", + "YES": "YES", + "NO": "NO", + "NEGATIVE": "NEGATIVE" + }, + "TOOLTIP": { + "EMAIL": "Email should be a valid email address like name@example.com.", + "USER_NAME": "Cannot contain special characters and maximum length should be 20 characters.", + "FULL_NAME": "Maximum length should be 20 characters.", + "COMMENT": "Length of comment should be less than 20 characters.", + "CURRENT_PWD": "Current password is required.", + "PASSWORD": "Password should be at least 8 characters with at least 1 uppercase, 1 lowercase and 1 number.", + "CONFIRM_PWD": "Passwords do not match.", + "SIGN_IN_USERNAME": "Username is required.", + "SIGN_IN_PWD": "Password is required.", + "SIGN_UP_MAIL": "Email is only used for resetting your password.", + "SIGN_UP_REAL_NAME": "First and last name", + "ITEM_REQUIRED": "Field is required.", + "NUMBER_REQUIRED": "Field is required and should be numbers.", + "PORT_REQUIRED": "Field is required and should be valid port number.", + "EMAIL_EXISTING": "Email address already exists.", + "USER_EXISTING": "Username is already in use." + }, + "PLACEHOLDER": { + "CURRENT_PWD": "Enter current password", + "NEW_PWD": "Enter new password", + "CONFIRM_PWD": "Confirm new password", + "USER_NAME": "Enter username", + "MAIL": "Enter email address", + "FULL_NAME": "Enter full name", + "SIGN_IN_NAME": "Username", + "SIGN_IN_PWD": "Password" + }, + "PROFILE": { + "TITLE": "User Profile", + "USER_NAME": "Username", + "EMAIL": "Email", + "FULL_NAME": "First and last name", + "COMMENT": "Comments", + "PASSWORD": "Password", + "SAVE_SUCCESS": "User profile saved successfully." + }, + "CHANGE_PWD": { + "TITLE": "Change Password", + "CURRENT_PWD": "Current Password", + "NEW_PWD": "New Password", + "CONFIRM_PWD": "Confirm Password", + "SAVE_SUCCESS": "User password changed successfully.", + "PASS_TIPS": "At least 8 characters with 1 uppercase, 1 lowercase and 1 number" + }, + "ACCOUNT_SETTINGS": { + "PROFILE": "User Profile", + "CHANGE_PWD": "Change Password", + "ABOUT": "About", + "LOGOUT": "Log Out", + "ROOT_CERT": "Download Root Cert" + }, + "GLOBAL_SEARCH": { + "PLACEHOLDER": "Search Harbor...", + "PLACEHOLDER_VIC": "Search Registry..." + }, + "SIDE_NAV": { + "DASHBOARD": "Dashboard", + "PROJECTS": "Projects", + "SYSTEM_MGMT": { + "NAME": "Administration", + "USER": "Users", + "REPLICATION": "Replication", + "CONFIG": "Configuration" + }, + "LOGS": "Logs" + }, + "USER": { + "ADD_ACTION": "USER", + "ENABLE_ADMIN_ACTION": "Set as Administrator", + "DISABLE_ADMIN_ACTION": "Revoke Administrator", + "DEL_ACTION": "Delete", + "FILTER_PLACEHOLDER": "Filter users", + "COLUMN_NAME": "Name", + "COLUMN_ADMIN": "Administrator", + "COLUMN_EMAIL": "Email", + "COLUMN_REG_NAME": "Registration time", + "IS_ADMIN": "Yes", + "IS_NOT_ADMIN": "No", + "ADD_USER_TITLE": "New User", + "SAVE_SUCCESS": "New user created successfully.", + "DELETION_TITLE": "Confirm user deletion", + "DELETION_SUMMARY": "Do you want to delete user {{param}}?", + "DELETE_SUCCESS": "User deleted successfully.", + "ITEMS": "item(s)" + }, + "PROJECT": { + "PROJECTS": "Projects", + "NAME": "Project Name", + "ROLE": "Role", + "PUBLIC_OR_PRIVATE": "Public", + "REPO_COUNT": "Repositories Count", + "CREATION_TIME": "Creation Time", + "PUBLIC": "Public", + "PRIVATE": "Private", + "MAKE": "Make", + "NEW_POLICY": "New Replication Rule", + "DELETE": "Delete", + "MY_PROJECTS": "All Projects", + "PUBLIC_PROJECTS": "Public Projects", + "PROJECT": "Project", + "NEW_PROJECT": "New Project", + "NAME_IS_REQUIRED": "Project name is required.", + "NAME_MINIMUM_LENGTH": "Project name is too short, it should be greater than 2 characters.", + "NAME_ALREADY_EXISTS": "Project name already exists.", + "NAME_IS_ILLEGAL": "Project name is invalid.", + "UNKNOWN_ERROR": "An unknown error occurred while creating the project.", + "ITEMS": "item(s)", + "DELETION_TITLE": "Confirm project deletion", + "DELETION_SUMMARY": "Do you want to delete project {{param}}?", + "FILTER_PLACEHOLDER": "Filter Projects", + "REPLICATION_RULE": "Replication Rule", + "CREATED_SUCCESS": "Created project successfully.", + "DELETED_SUCCESS": "Deleted project successfully.", + "TOGGLED_SUCCESS": "Toggled project successfully.", + "FAILED_TO_DELETE_PROJECT": "Project contains repositories or replication rules cannot be deleted.", + "INLINE_HELP_PUBLIC": "When a project is set to public, anyone has read permission to the repositories under this project, and the user does not need to run \"docker login\" before pulling images under this project." + }, + "PROJECT_DETAIL": { + "REPOSITORIES": "Repositories", + "REPLICATION": "Replication", + "USERS": "Members", + "LOGS": "Logs", + "PROJECTS": "Projects" + }, + "MEMBER": { + "NEW_MEMBER": "New Member", + "MEMBER": "Member", + "NAME": "Name", + "ROLE": "Role", + "SYS_ADMIN": "System Admin", + "PROJECT_ADMIN": "Project Admin", + "DEVELOPER": "Developer", + "GUEST": "Guest", + "DELETE": "Delete", + "ITEMS": "item(s)", + "ACTIONS": "Actions", + "USERNAME_IS_REQUIRED": "Username is required", + "USERNAME_DOES_NOT_EXISTS": "Username does not exist.", + "USERNAME_ALREADY_EXISTS": "Username already exists.", + "UNKNOWN_ERROR": "Unknown error occurred while adding member.", + "FILTER_PLACEHOLDER": "Filter Members", + "DELETION_TITLE": "Confirm project member deletion", + "DELETION_SUMMARY": "Do you want to delete project member {{param}}?", + "ADDED_SUCCESS": "Added member successfully.", + "DELETED_SUCCESS": "Deleted member successfully.", + "SWITCHED_SUCCESS": "Switched member role successfully." + }, + "AUDIT_LOG": { + "USERNAME": "Username", + "REPOSITORY_NAME": "Repository Name", + "TAGS": "Tags", + "OPERATION": "Operation", + "OPERATIONS": "Operations", + "TIMESTAMP": "Timestamp", + "ALL_OPERATIONS": "All Operations", + "PULL": "Pull", + "PUSH": "Push", + "CREATE": "Create", + "DELETE": "Delete", + "OTHERS": "Others", + "ADVANCED": "Advanced", + "SIMPLE": "Simple", + "ITEMS": "item(s)", + "FILTER_PLACEHOLDER": "Filter Logs" + }, + "REPLICATION": { + "REPLICATION_RULE": "Replication Rule", + "NEW_REPLICATION_RULE": "New Replication Rule", + "ENDPOINTS": "Endpoints", + "FILTER_POLICIES_PLACEHOLDER": "Filter Rules", + "FILTER_JOBS_PLACEHOLDER": "Filter Jobs", + "DELETION_TITLE": "Confirm Rule Deletion", + "DELETION_SUMMARY": "Do you want to delete rule {{param}}?", + "FILTER_TARGETS_PLACEHOLDER": "Filter Endpoints", + "DELETION_TITLE_TARGET": "Confirm Endpoint Deletion", + "DELETION_SUMMARY_TARGET": "Do you want to delete the endpoint {{param}}?", + "ADD_POLICY": "New Replication Rule", + "EDIT_POLICY": "Edit", + "EDIT_POLICY_TITLE": "Edit Replication Rule", + "DELETE_POLICY": "Delete", + "TEST_CONNECTION": "Test Connection", + "TESTING_CONNECTION": "Testing Connection...", + "TEST_CONNECTION_SUCCESS": "Connection tested successfully.", + "TEST_CONNECTION_FAILURE": "Failed to ping endpoint.", + "NAME": "Name", + "PROJECT": "Project", + "NAME_IS_REQUIRED": "Name is required.", + "DESCRIPTION": "Description", + "ENABLE": "Enable", + "DISABLE": "Disable", + "DESTINATION_NAME": "Endpoint Name", + "DESTINATION_NAME_IS_REQUIRED": "Endpoint name is required.", + "NEW_DESTINATION": "New Endpoint", + "DESTINATION_URL": "Endpoint URL", + "DESTINATION_URL_IS_REQUIRED": "Endpoint URL is required.", + "DESTINATION_USERNAME": "Username", + "DESTINATION_PASSWORD": "Password", + "ALL_STATUS": "All Status", + "ENABLED": "Enabled", + "DISABLED": "Disabled", + "LAST_START_TIME": "Last Start Time", + "ACTIVATION": "Activation", + "REPLICATION_JOBS": "Replication Jobs", + "ALL": "All", + "PENDING": "Pending", + "RUNNING": "Running", + "ERROR": "Error", + "RETRYING": "Retrying", + "STOPPED": "Stopped", + "FINISHED": "Finished", + "CANCELED": "Canceled", + "SIMPLE": "Simple", + "ADVANCED": "Advanced", + "STATUS": "Status", + "OPERATION": "Operation", + "CREATION_TIME": "Start Time", + "END_TIME": "End Time", + "LOGS": "Logs", + "ITEMS": "item(s)", + "TOGGLE_ENABLE_TITLE": "Enable Rule", + "CONFIRM_TOGGLE_ENABLE_POLICY": "After enabling the replication rule, all repositories under the project will be replicated to the destination registry. \nPlease confirm to continue.", + "TOGGLE_DISABLE_TITLE": "Disable Rule", + "CONFIRM_TOGGLE_DISABLE_POLICY": "After disabling the rule, all unfinished replication jobs of this rule will be stopped and canceled. \nPlease confirm to continue.", + "CREATED_SUCCESS": "Created replication rule successfully.", + "UPDATED_SUCCESS": "Updated replication rule successfully.", + "DELETED_SUCCESS": "Deleted replication rule successfully.", + "DELETED_FAILED": "Deleted replication rule failed.", + "TOGGLED_SUCCESS": "Toggled replication rule status successfully.", + "CANNOT_EDIT": "Replication rule cannot be changed while it is enabled.", + "POLICY_ALREADY_EXISTS": "Replication rule already exists.", + "FAILED_TO_DELETE_POLICY_ENABLED": "Cannot delete rule: rule has unfinished job(s) or rule is enabled.", + "FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check." + }, + "DESTINATION": { + "NEW_ENDPOINT": "New Endpoint", + "ENDPOINT": "Endpoint", + "NAME": "Endpoint Name", + "NAME_IS_REQUIRED": "Endpoint name is required.", + "URL": "Endpoint URL", + "URL_IS_REQUIRED": "Endpoint URL is required.", + "USERNAME": "Username", + "PASSWORD": "Password", + "TEST_CONNECTION": "Test Connection", + "TITLE_EDIT": "Edit Endpoint", + "TITLE_ADD": "Create Endpoint", + "DELETE": "Delete Endpoint", + "TESTING_CONNECTION": "Testing Connection...", + "TEST_CONNECTION_SUCCESS": "Connection tested successfully.", + "TEST_CONNECTION_FAILURE": "Failed to ping endpoint.", + "CONFLICT_NAME": "Endpoint name or URL already exists.", + "INVALID_NAME": "Invalid endpoint name.", + "FAILED_TO_GET_TARGET": "Failed to get endpoint.", + "CREATION_TIME": "Creation Time", + "ITEMS": "item(s)", + "CREATED_SUCCESS": "Created endpoint successfully.", + "UPDATED_SUCCESS": "Updated endpoint successfully.", + "DELETED_SUCCESS": "Deleted endpoint successfully.", + "DELETED_FAILED": "Deleted endpoint failed.", + "CANNOT_EDIT": "Endpoint cannot be changed while the replication rule is enabled.", + "FAILED_TO_DELETE_TARGET_IN_USED": "Failed to delete the endpoint in use." + }, + "REPOSITORY": { + "COPY_ID": "Copy ID", + "COPY_PARENT_ID": "Copy Parent ID", + "DELETE": "Delete", + "NAME": "Name", + "TAGS_COUNT": "Tags", + "PULL_COUNT": "Pulls", + "PULL_COMMAND": "Pull Command", + "MY_REPOSITORY": "My Repository", + "PUBLIC_REPOSITORY": "Public Repository", + "DELETION_TITLE_REPO": "Confirm Repository Deletion", + "DELETION_SUMMARY_REPO": "Do you want to delete repository {{param}}?", + "DELETION_TITLE_TAG": "Confirm Tag Deletion", + "DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?", + "DELETION_TITLE_TAG_DENIED": "Signed tag cannot be deleted", + "DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from the Notary before it can be deleted.\nDelete from Notary via this command:\n{{param}}", + "FILTER_FOR_REPOSITORIES": "Filter Repositories", + "TAG": "Tag", + "SIGNED": "Signed", + "AUTHOR": "Author", + "CREATED": "Creation Time", + "DOCKER_VERSION": "Docker Version", + "ARCHITECTURE": "Architecture", + "OS": "OS", + "SHOW_DETAILS": "Show Details", + "REPOSITORIES": "Repositories", + "ITEMS": "item(s)", + "POP_REPOS": "Popular Repositories", + "DELETED_REPO_SUCCESS": "Deleted repository successfully.", + "DELETED_TAG_SUCCESS": "Deleted tag successfully.", + "COPY": "Copy" + }, + "ALERT": { + "FORM_CHANGE_CONFIRMATION": "Some changes are not saved yet. Do you want to cancel?" + }, + "RESET_PWD": { + "TITLE": "Reset Password", + "CAPTION": "Enter your email to reset your password", + "EMAIL": "Email", + "SUCCESS": "Mail with password resetting link is successfully sent. You can close this dialog and check your mailbox.", + "CAPTION2": "Enter your new password", + "RESET_OK": "Password has been successfully reset. Click OK to login with new password." + }, + "RECENT_LOG": { + "SUB_TITLE": "Show", + "SUB_TITLE_SUFIX": "logs" + }, + "CONFIG": { + "TITLE": "Configuration", + "AUTH": "Authentication", + "REPLICATION": "Replication", + "EMAIL": "Email", + "SYSTEM": "System Settings", + "CONFIRM_TITLE": "Confirm to cancel", + "CONFIRM_SUMMARY": "Some changes have not been saved. Do you want to discard them?", + "SAVE_SUCCESS": "Configuration has been successfully saved.", + "MAIL_SERVER": "Email Server", + "MAIL_SERVER_PORT": "Email Server Port", + "MAIL_USERNAME": "Email Username", + "MAIL_PASSWORD": "Email Password", + "MAIL_FROM": "Email From", + "MAIL_SSL": "Email SSL", + "SSL_TOOLTIP": "Enable SSL for email server connection", + "VERIFY_REMOTE_CERT": "Verify Remote Cert", + "TOKEN_EXPIRATION": "Token Expiration (Minutes)", + "AUTH_MODE": "Auth Mode", + "PRO_CREATION_RESTRICTION": "Project Creation", + "SELF_REGISTRATION": "Allow Self-Registration", + "AUTH_MODE_DB": "Database", + "AUTH_MODE_LDAP": "LDAP", + "SCOPE_BASE": "Base", + "SCOPE_ONE_LEVEL": "OneLevel", + "SCOPE_SUBTREE": "Subtree", + "PRO_CREATION_EVERYONE": "Everyone", + "PRO_CREATION_ADMIN": "Admin Only", + "TOOLTIP": { + "SELF_REGISTRATION": "Enable sign up.", + "VERIFY_REMOTE_CERT": "Determine whether the image replication should verify the certificate of a remote Harbor registry. Uncheck this box when the remote registry uses a self-signed or untrusted certificate.", + "AUTH_MODE": "By default the authentication mode is database, i.e. the credentials are stored in a local database. Set it to LDAP if you want to verify a user's credential against an LDAP server.", + "LDAP_SEARCH_DN": "A user's DN who has the permission to search the LDAP/AD server. If your LDAP/AD server does not support anonymous search, you should configure this DN and ldap_search_pwd.", + "LDAP_BASE_DN": "The base DN from which to look up a user in LDAP/AD.", + "LDAP_UID": "The attribute used in a search to match a user. It could be uid, cn, email, sAMAccountName or other attributes depending on your LDAP/AD.", + "LDAP_SCOPE": "The scope to search for users", + "TOKEN_EXPIRATION": "The expiration time (in minutes) of a token created by the token service. Default is 30 minutes.", + "PRO_CREATION_RESTRICTION": "The flag to define what users have permission to create projects. By default, everyone can create a project. Set to 'Admin Only' so that only an administrator can create a project." + }, + "LDAP": { + "URL": "LDAP URL", + "SEARCH_DN": "LDAP Search DN", + "SEARCH_PWD": "LDAP Search Password", + "BASE_DN": "LDAP Base DN", + "FILTER": "LDAP Filter", + "UID": "LDAP UID", + "SCOPE": "LDAP Scope" + }, + "TEST_MAIL_SUCCESS": "Connection to mail server is verified.", + "TEST_LDAP_SUCCESS": "Connection to LDAP server is verified.", + "TEST_MAIL_FAILED": "Failed to verify mail server with error: {{param}}.", + "TEST_LDAP_FAILED": "Failed to verify LDAP server with error: {{param}}.", + "LEAVING_CONFIRMATION_TITLE": "Confirm to leave", + "LEAVING_CONFIRMATION_SUMMARY": "Changes have not been saved yet. Do you want to leave current page?" + }, + "PAGE_NOT_FOUND": { + "MAIN_TITLE": "Page not found", + "SUB_TITLE": "Redirecting to main page in", + "UNIT": "seconds..." + }, + "ABOUT": { + "VERSION": "Version", + "BUILD": "Build", + "COPYRIGHT": "Copyright 1998-2017 VMware, Inc. All rights reserved. This product is protected by U.S. and international property laws. VMware products are covered by one or more patents listed at", + "COPYRIGHT_SUFIX": ".", + "TRADEMARK": "VMware is a registered trademark or trademark of VMware, Inc. in the United States and other jurisdictions. All other marks and names mentioned herein may be trademark of their respective companies.", + "END_USER_LICENSE": "End User License Agreement", + "OPEN_SOURCE_LICENSE": "Open Source/Third Party License" + }, + "START_PAGE": { + "GETTING_START": "", + "GETTING_START_TITLE": "Getting Started" + }, + "TOP_REPO": "Popular Repositories", + "STATISTICS": { + "TITLE": "STATISTICS", + "PRO_ITEM": "PROJECTS", + "REPO_ITEM": "REPOSITORIES", + "INDEX_MY": "MY", + "INDEX_PUB": "PUBLIC", + "INDEX_TOTAL": "TOTAL", + "STORAGE": "STORAGE", + "LIMIT": "Limit" + }, + "SEARCH": { + "IN_PROGRESS": "Search...", + "BACK": "Back" + }, + "UNKNOWN_ERROR": "Unknown errors have occurred. Please try again later.", + "UNAUTHORIZED_ERROR": "Your session is invalid or has expired. You need to sign in to continue your action.", + "FORBIDDEN_ERROR": "You do not have the proper privileges to perform the action.", + "GENERAL_ERROR": "Errors have occurred when performing service call: {{param}}.", + "BAD_REQUEST_ERROR": "We are unable to perform your action because of a bad request.", + "NOT_FOUND_ERROR": "Your request cannot be completed because the object does not exist.", + "CONFLICT_ERROR": "We are unable to perform your action because your submission has conflicts.", + "PRECONDITION_FAILED": "We are unable to perform your action because of a precondition failure.", + "SERVER_ERROR": "We are unable to perform your action because internal server errors have occurred.", + "INCONRRECT_OLD_PWD": "The old password is incorrect.", + "UNKNOWN": "n/a" +} \ No newline at end of file diff --git a/src/ui_ng/src/i18n/lang/zh-cn-lang.json b/src/ui_ng/src/i18n/lang/zh-cn-lang.json new file mode 100644 index 000000000..57aba1bcf --- /dev/null +++ b/src/ui_ng/src/i18n/lang/zh-cn-lang.json @@ -0,0 +1,444 @@ +{ + "APP_TITLE": { + "VMW_HARBOR": "VMware Harbor", + "HARBOR": "Harbor", + "VIC": "vSphere Integrated Containers", + "MGMT": "Management", + "REG": "Registry" + }, + "SIGN_IN": { + "REMEMBER": "记住我", + "INVALID_MSG": "用户名或者密码不正确。", + "FORGOT_PWD": "忘记密码", + "HEADER_LINK": "登录" + }, + "SIGN_UP": { + "TITLE": "注册" + }, + "BUTTON": { + "CANCEL": "取消", + "OK": "确定", + "DELETE": "删除", + "LOG_IN": "登录", + "SIGN_UP_LINK": "注册账号", + "SIGN_UP": "注册", + "CONFIRM": "确定", + "SEND": "发送", + "SAVE": "保存", + "TEST_MAIL": "测试邮件服务器", + "CLOSE": "关闭", + "TEST_LDAP": "测试LDAP服务器", + "MORE_INFO": "更多信息...", + "YES": "是", + "NO": "否", + "NEGATIVE": "否" + }, + "TOOLTIP": { + "EMAIL": "请使用正确的邮箱地址,比如name@example.com。", + "USER_NAME": "不能包含特殊字符且长度不能超过20。", + "FULL_NAME": "长度不能超过20。", + "COMMENT": "长度不能超过20。", + "CURRENT_PWD": "当前密码为必填项。", + "PASSWORD": "密码长度至少为8且需包含至少一个大写字符,一个小写字符和一个数字。", + "CONFIRM_PWD": "密码输入不一致。", + "SIGN_IN_USERNAME": "用户名为必填项。", + "SIGN_IN_PWD": "密码为必填项。", + "SIGN_UP_MAIL": "邮件地址仅用来重置您的密码。", + "SIGN_UP_REAL_NAME": "全名", + "ITEM_REQUIRED": "此项为必填项。", + "NUMBER_REQUIRED": "此项为必填项且为数字。", + "PORT_REQUIRED": "此项为必填项且为合法端口号。", + "EMAIL_EXISTING": "邮件地址已经存在。", + "USER_EXISTING": "用户名已经存在。" + }, + "PLACEHOLDER": { + "CURRENT_PWD": "输入当前密码", + "NEW_PWD": "输入新密码", + "CONFIRM_PWD": "确认新密码", + "USER_NAME": "输入用户名称", + "MAIL": "输入邮箱地址", + "FULL_NAME": "输入全名", + "SIGN_IN_NAME": "用户名", + "SIGN_IN_PWD": "密码" + }, + "PROFILE": { + "TITLE": "用户设置", + "USER_NAME": "用户名", + "EMAIL": "邮箱", + "FULL_NAME": "全名", + "COMMENT": "注释", + "PASSWORD": "密码", + "SAVE_SUCCESS": "成功保存用户设置。" + }, + "CHANGE_PWD": { + "TITLE": "修改密码", + "CURRENT_PWD": "当前密码", + "NEW_PWD": "新密码", + "CONFIRM_PWD": "确认密码", + "SAVE_SUCCESS": "成功更改用户密码。", + "PASS_TIPS": "至少8个字符且需包含至少一个大写字符、小写字符或者数字" + }, + "ACCOUNT_SETTINGS": { + "PROFILE": "用户设置", + "CHANGE_PWD": "修改密码", + "ABOUT": "关于", + "LOGOUT": "退出", + "ROOT_CERT": "下载根证书" + }, + "GLOBAL_SEARCH": { + "PLACEHOLDER": "搜索 Harbor...", + "PLACEHOLDER_VIC": "搜索 Registry..." + }, + "SIDE_NAV": { + "DASHBOARD": "仪表板", + "PROJECTS": "项目", + "SYSTEM_MGMT": { + "NAME": "系统管理", + "USER": "用户管理", + "REPLICATION": "复制管理", + "CONFIG": "配置管理" + }, + "LOGS": "日志" + }, + "USER": { + "ADD_ACTION": "用户", + "ENABLE_ADMIN_ACTION": "设置为管理员", + "DISABLE_ADMIN_ACTION": "取消管理员", + "DEL_ACTION": "删除", + "FILTER_PLACEHOLDER": "过滤用户", + "COLUMN_NAME": "用户名", + "COLUMN_ADMIN": "管理员", + "COLUMN_EMAIL": "邮件", + "COLUMN_REG_NAME": "注册时间", + "IS_ADMIN": "是", + "IS_NOT_ADMIN": "否", + "ADD_USER_TITLE": "创建用户", + "SAVE_SUCCESS": "成功创建用户。", + "DELETION_TITLE": "删除用户确认", + "DELETION_SUMMARY": "你确认删除用户 {{param}}?", + "DELETE_SUCCESS": "成功删除用户。", + "ITEMS": "条记录" + }, + "PROJECT": { + "PROJECTS": "项目", + "NAME": "项目名称", + "ROLE": "角色", + "PUBLIC_OR_PRIVATE": "公开", + "REPO_COUNT": "镜像仓库数", + "CREATION_TIME": "创建时间", + "PUBLIC": "公开", + "PRIVATE": "私有", + "MAKE": "设为", + "NEW_POLICY": "新建规则", + "DELETE": "删除", + "MY_PROJECTS": "所有项目", + "PUBLIC_PROJECTS": "公开项目", + "PROJECT": "项目", + "NEW_PROJECT": "新建项目", + "NAME_IS_REQUIRED": "项目名称为必填项", + "NAME_MINIMUM_LENGTH": "项目名称长度过短,至少多于2个字符。", + "NAME_ALREADY_EXISTS": "项目名称已存在。", + "NAME_IS_ILLEGAL": "项目名称非法。", + "UNKNOWN_ERROR": "创建项目时发生未知错误。", + "ITEMS": "条记录", + "DELETION_TITLE": "删除项目确认", + "DELETION_SUMMARY": "你确认删除项目 {{param}}?", + "FILTER_PLACEHOLDER": "过滤项目", + "REPLICATION_RULE": "复制规则", + "CREATED_SUCCESS": "成功创建项目。", + "DELETED_SUCCESS": "成功删除项目。", + "TOGGLED_SUCCESS": "切换状态成功。", + "FAILED_TO_DELETE_PROJECT": "项目包含镜像仓库或复制规则,无法删除。", + "INLINE_HELP_PUBLIC": "当项目设为公开后,任何人都有此项目下镜像的读权限。命令行用户不需要“docker login”就可以拉取此项目下的镜像。" + }, + "PROJECT_DETAIL": { + "REPOSITORIES": "镜像仓库", + "REPLICATION": "复制", + "USERS": "成员", + "LOGS": "日志", + "PROJECTS": "项目" + }, + "MEMBER": { + "NEW_MEMBER": "新建成员", + "MEMBER": "成员", + "NAME": "姓名", + "ROLE": "角色", + "SYS_ADMIN": "系统管理员", + "PROJECT_ADMIN": "项目管理员", + "DEVELOPER": "开发人员", + "GUEST": "访客", + "DELETE": "删除", + "ITEMS": "条记录", + "ACTIONS": "操作", + "USERNAME_IS_REQUIRED": "用户名为必填项。", + "USERNAME_DOES_NOT_EXISTS": "用户名不存在。", + "USERNAME_ALREADY_EXISTS": "用户名已存在。", + "UNKNOWN_ERROR": "添加成员时发生未知错误。", + "FILTER_PLACEHOLDER": "过滤成员", + "DELETION_TITLE": "删除项目成员确认", + "DELETION_SUMMARY": "你确认删除项目成员 {{param}}?", + "ADDED_SUCCESS": "成功新增成员。", + "DELETED_SUCCESS": "成功删除成员。", + "SWITCHED_SUCCESS": "切换角色成功。" + }, + "AUDIT_LOG": { + "USERNAME": "用户名", + "REPOSITORY_NAME": "镜像名称", + "TAGS": "标签", + "OPERATION": "操作", + "OPERATIONS": "操作", + "TIMESTAMP": "时间戳", + "ALL_OPERATIONS": "所有操作", + "PULL": "Pull", + "PUSH": "Push", + "CREATE": "Create", + "DELETE": "Delete", + "OTHERS": "其他", + "ADVANCED": "高级检索", + "SIMPLE": "简单检索", + "ITEMS": "条记录", + "FILTER_PLACEHOLDER": "过滤日志" + }, + "REPLICATION": { + "REPLICATION_RULE": "复制规则", + "NEW_REPLICATION_RULE": "新建规则", + "ENDPOINTS": "目标", + "FILTER_POLICIES_PLACEHOLDER": "过滤规则", + "FILTER_JOBS_PLACEHOLDER": "过滤任务", + "DELETION_TITLE": "删除规则确认", + "DELETION_SUMMARY": "确认删除规则 {{param}}?", + "FILTER_TARGETS_PLACEHOLDER": "过滤目标", + "DELETION_TITLE_TARGET": "删除目标确认", + "DELETION_SUMMARY_TARGET": "确认删除目标 {{param}}?", + "ADD_POLICY": "新建规则", + "EDIT_POLICY": "修改", + "EDIT_POLICY_TITLE": "修改规则", + "DELETE_POLICY": "删除", + "TEST_CONNECTION": "测试连接", + "TESTING_CONNECTION": "正在测试连接...", + "TEST_CONNECTION_SUCCESS": "测试连接成功。", + "TEST_CONNECTION_FAILURE": "测试连接失败。", + "NAME": "名称", + "PROJECT": "项目", + "NAME_IS_REQUIRED": "名称为必填项。", + "DESCRIPTION": "描述", + "ENABLE": "启用", + "DISABLE": "停用", + "DESTINATION_NAME": "目标名", + "DESTINATION_NAME_IS_REQUIRED": "目标名称为必填项。", + "NEW_DESTINATION": "创建目标", + "DESTINATION_URL": "目标URL", + "DESTINATION_URL_IS_REQUIRED": "目标URL为必填项。", + "DESTINATION_USERNAME": "用户名", + "DESTINATION_PASSWORD": "密码", + "ALL_STATUS": "所有状态", + "ENABLED": "启用", + "DISABLED": "停用", + "LAST_START_TIME": "上次起始时间", + "ACTIVATION": "活动状态", + "REPLICATION_JOBS": "复制任务", + "ALL": "全部", + "PENDING": "挂起", + "RUNNING": "运行中", + "ERROR": "错误", + "RETRYING": "重试中", + "STOPPED": "已停止", + "FINISHED": "已完成", + "CANCELED": "已取消", + "SIMPLE": "简单检索", + "ADVANCED": "高级检索", + "STATUS": "状态", + "OPERATION": "操作", + "CREATION_TIME": "创建时间", + "END_TIME": "结束时间", + "LOGS": "日志", + "ITEMS": "条记录", + "TOGGLE_ENABLE_TITLE": "启用规则", + "CONFIRM_TOGGLE_ENABLE_POLICY": "启用规则后,该项目下的所有镜像仓库将复制到目标实例。\n请确认继续。", + "TOGGLE_DISABLE_TITLE": "停用规则", + "CONFIRM_TOGGLE_DISABLE_POLICY": "停用规则后,所有未完成的复制任务将被终止和取消。\n请确认继续。", + "CREATED_SUCCESS": "创建复制规则成功。", + "UPDATED_SUCCESS": "更新复制规则成功。", + "DELETED_SUCCESS": "删除复制规则成功。", + "DELETED_FAILED": "删除复制规则失败。", + "TOGGLED_SUCCESS": "切换复制规则状态成功。", + "CANNOT_EDIT": "当复制规则启用时无法修改。", + "POLICY_ALREADY_EXISTS": "规则已存在。", + "FAILED_TO_DELETE_POLICY_ENABLED": "删除复制规则失败: 仍有未完成的任务或规则未停用。", + "FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。" + }, + "DESTINATION": { + "NEW_ENDPOINT": "新建目标", + "ENDPOINT": "目标", + "NAME": "目标名", + "NAME_IS_REQUIRED": "目标名为必填项。", + "URL": "目标URL", + "URL_IS_REQUIRED": "目标URL为必填项。", + "USERNAME": "用户名", + "PASSWORD": "密码", + "TEST_CONNECTION": "测试连接", + "TITLE_EDIT": "编辑目标", + "TITLE_ADD": "新建目标", + "DELETE": "删除目标", + "TESTING_CONNECTION": "正在测试连接...", + "TEST_CONNECTION_SUCCESS": "测试连接成功。", + "TEST_CONNECTION_FAILURE": "测试连接失败。", + "CONFLICT_NAME": "目标名或目标URL已存在。", + "INVALID_NAME": "无效的目标名称。", + "FAILED_TO_GET_TARGET": "获取目标失败。", + "CREATION_TIME": "创建时间", + "ITEMS": "条记录", + "CREATED_SUCCESS": "成功创建目标。", + "UPDATED_SUCCESS": "成功更新目标。", + "DELETED_SUCCESS": "成功删除目标。", + "DELETED_FAILED": "删除目标失败。", + "CANNOT_EDIT": "当复制规则启用时目标无法修改。", + "FAILED_TO_DELETE_TARGET_IN_USED": "无法删除正在使用的目标。" + }, + "REPOSITORY": { + "COPY_ID": "复制ID", + "COPY_PARENT_ID": "复制父级ID", + "DELETE": "删除", + "NAME": "名称", + "TAGS_COUNT": "标签数", + "PULL_COUNT": "下载数", + "PULL_COMMAND": "Pull命令", + "MY_REPOSITORY": "我的仓库", + "PUBLIC_REPOSITORY": "公共仓库", + "DELETION_TITLE_REPO": "删除镜像仓库确认", + "DELETION_SUMMARY_REPO": "确认删除镜像仓库 {{param}}?", + "DELETION_TITLE_TAG": "删除镜像标签确认", + "DELETION_SUMMARY_TAG": "确认删除镜像标签 {{param}}?", + "DELETION_TITLE_TAG_DENIED": "已签名的镜像不能被删除", + "DELETION_SUMMARY_TAG_DENIED": "要删除此镜像标签必须首先从Notary中删除。\n请执行如下Notary命令删除:\n{{param}}", + "FILTER_FOR_REPOSITORIES": "过滤镜像仓库", + "TAG": "标签", + "SIGNED": "已签名", + "AUTHOR": "作者", + "CREATED": "创建时间", + "DOCKER_VERSION": "Docker版本", + "ARCHITECTURE": "架构", + "OS": "操作系统", + "SHOW_DETAILS": "显示详细", + "REPOSITORIES": "镜像仓库", + "ITEMS": "条记录", + "POP_REPOS": "受欢迎的镜像仓库", + "DELETED_REPO_SUCCESS": "成功删除镜像仓库。", + "DELETED_TAG_SUCCESS": "成功删除镜像标签。", + "COPY": "复制" + }, + "ALERT": { + "FORM_CHANGE_CONFIRMATION": "表单内容改变,确认是否取消?" + }, + "RESET_PWD": { + "TITLE": "重置密码", + "CAPTION": "输入用来重置密码的邮箱", + "EMAIL": "邮箱", + "SUCCESS": "重置密码邮件已成功发送. 请关闭对话框并检查邮箱。", + "CAPTION2": "请输入您的新密码", + "RESET_OK": "密码重置成功,点击确定按钮重新登录。" + }, + "RECENT_LOG": { + "SUB_TITLE": "显示", + "SUB_TITLE_SUFIX": "条日志" + }, + "CONFIG": { + "TITLE": "配置", + "AUTH": "认证模式", + "REPLICATION": "复制", + "EMAIL": "邮箱", + "SYSTEM": "系统设置", + "CONFIRM_TITLE": "确认取消", + "CONFIRM_SUMMARY": "配置项有改动, 确定取消?", + "SAVE_SUCCESS": "变更的配置项成功保存。", + "MAIL_SERVER": "邮件服务器", + "MAIL_SERVER_PORT": "邮件服务器端口", + "MAIL_USERNAME": "用户名", + "MAIL_PASSWORD": "密码", + "MAIL_FROM": "邮件来源", + "MAIL_SSL": "邮件 SSL", + "SSL_TOOLTIP": "启用SSL到邮件服务器连接。", + "VERIFY_REMOTE_CERT": "验证远程证书", + "TOKEN_EXPIRATION": "令牌过期时间(分钟)", + "AUTH_MODE": "认证模式", + "PRO_CREATION_RESTRICTION": "项目创建", + "SELF_REGISTRATION": "允许自注册", + "AUTH_MODE_DB": "数据库", + "AUTH_MODE_LDAP": "LDAP", + "SCOPE_BASE": "本层", + "SCOPE_ONE_LEVEL": "下一层", + "SCOPE_SUBTREE": "子树", + "PRO_CREATION_EVERYONE": "所有人", + "PRO_CREATION_ADMIN": "仅管理员", + "TOOLTIP": { + "SELF_REGISTRATION": "激活注册功能。", + "VERIFY_REMOTE_CERT": "确定镜像复制是否要验证远程Harbor实例的证书。如果远程实例使用的是自签或者非信任证书,不要勾选此项。", + "AUTH_MODE": "默认认证模式为数据库认证,即用户凭证存储在本地数据库。如果使用LDAP来认证用户,则设置为LDAP。", + "LDAP_SEARCH_DN": "有搜索权限的LDAP用户DN。如果LDAP服务器不支持匿名搜索,则需要配置此DN及其密码。", + "LDAP_BASE_DN": "用来在LDAP和AD中搜寻用户的基础DN。", + "LDAP_UID": "在搜索中用来匹配用户的属性,可以是uid,cn,email,sAMAccountName或者其它LDAP/AD服务器支持的属性。", + "LDAP_SCOPE": "搜索用户的范围。", + "TOKEN_EXPIRATION": "由令牌服务创建的令牌的过期时间(分钟),默认为30分钟。", + "PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。" + }, + "LDAP": { + "URL": "LDAP URL", + "SEARCH_DN": "LDAP搜索DN", + "SEARCH_PWD": "LDAP搜索密码", + "BASE_DN": "LDAP基础DN", + "FILTER": "LDAP过滤器", + "UID": "LDAP用户UID的属性", + "SCOPE": "LDAP搜索范围" + }, + "TEST_MAIL_SUCCESS": "邮件服务器的连通正常。", + "TEST_LDAP_SUCCESS": "LDAP服务器的连通正常。", + "TEST_MAIL_FAILED": "验证邮件服务器失败,错误: {{param}}。", + "TEST_LDAP_FAILED": "验证LDAP服务器失败,错误: {{param}}。", + "LEAVING_CONFIRMATION_TITLE": "确定离开", + "LEAVING_CONFIRMATION_SUMMARY": "有未保存的配置更改, 确认离开当前页面?" + }, + "PAGE_NOT_FOUND": { + "MAIN_TITLE": "页面不存在", + "SUB_TITLE": "正在重定向到首页:", + "UNIT": "秒..." + }, + "ABOUT": { + "VERSION": "版本", + "BUILD": "构建", + "COPYRIGHT": "版权所有 © 1998-2017 VMware, Inc. 保留所有权利。此产品受美国及其他国家/地区的版权和知识产权以及国际条约保护。VMware产品受", + "COPYRIGHT_SUFIX": "上列出的一项或多项专利保护。", + "TRADEMARK": "VMware徽标及设计都是VMware, Inc.在美国和/或其他法律辖区的注册商标或者商标。此处提到的其他所有商标和名称分别是其各自公司的商标。", + "END_USER_LICENSE": "终端用户许可协议", + "OPEN_SOURCE_LICENSE": "开源/第三方许可协议" + }, + "START_PAGE": { + "GETTING_START": "", + "GETTING_START_TITLE": "从这开始" + }, + "TOP_REPO": "受欢迎的镜像仓库", + "STATISTICS": { + "TITLE": "统计", + "PRO_ITEM": "项目", + "REPO_ITEM": "镜像仓库", + "INDEX_MY": "私有", + "INDEX_PUB": "公开", + "INDEX_TOTAL": "总计", + "STORAGE": "存储", + "LIMIT": "容量" + }, + "SEARCH": { + "IN_PROGRESS": "搜索中...", + "BACK": "返回" + }, + "UNKNOWN_ERROR": "发生未知错误,请稍后再试。", + "UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续。", + "FORBIDDEN_ERROR": "当前操作被禁止,请确认你有合法的权限。", + "GENERAL_ERROR": "调用后台服务时出现错误: {{param}}。", + "BAD_REQUEST_ERROR": "错误请求, 操作无法完成。", + "NOT_FOUND_ERROR": "对象不存在, 请求无法完成。", + "CONFLICT_ERROR": "请求包含冲突, 操作无法完成。", + "PRECONDITION_FAILED": "验证前置条件失败, 无法执行操作。", + "SERVER_ERROR": "服务器出现内部错误,请求无法完成。", + "INCONRRECT_OLD_PWD": "旧密码不正确。", + "UNKNOWN": "未知" +} \ No newline at end of file diff --git a/src/ui_ng/src/images/harbor-logo.png b/src/ui_ng/src/images/harbor-logo.png new file mode 100644 index 000000000..73cf7df2d Binary files /dev/null and b/src/ui_ng/src/images/harbor-logo.png differ diff --git a/src/ui_ng/src/index.html b/src/ui_ng/src/index.html new file mode 100644 index 000000000..2a3bf2fe3 --- /dev/null +++ b/src/ui_ng/src/index.html @@ -0,0 +1,20 @@ + + + + + + VMware + + + + + + + +
+ Loading... +
+
+ + + \ No newline at end of file diff --git a/src/ui_ng/src/main.ts b/src/ui_ng/src/main.ts new file mode 100644 index 000000000..ea8b45bcc --- /dev/null +++ b/src/ui_ng/src/main.ts @@ -0,0 +1,25 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import './polyfills.ts'; + +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { enableProdMode } from '@angular/core'; +import { environment } from './environments/environment'; +import { AppModule } from './app/'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/ui_ng/src/polyfills.ts b/src/ui_ng/src/polyfills.ts new file mode 100644 index 000000000..75db19a93 --- /dev/null +++ b/src/ui_ng/src/polyfills.ts @@ -0,0 +1,35 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// This file includes polyfills needed by Angular 2 and is loaded before +// the app. You can add your own extra polyfills to this file. +import 'core-js/es6/symbol'; +import 'core-js/es6/object'; +import 'core-js/es6/function'; +import 'core-js/es6/parse-int'; +import 'core-js/es6/parse-float'; +import 'core-js/es6/number'; +import 'core-js/es6/math'; +import 'core-js/es6/string'; +import 'core-js/es6/date'; +import 'core-js/es6/array'; +import 'core-js/es6/regexp'; +import 'core-js/es6/map'; +import 'core-js/es6/set'; +import 'core-js/es6/reflect'; + +import 'core-js/es7/reflect'; + + +import 'zone.js/dist/zone'; + diff --git a/src/ui_ng/src/styles.css b/src/ui_ng/src/styles.css new file mode 100644 index 000000000..a2f595bf9 --- /dev/null +++ b/src/ui_ng/src/styles.css @@ -0,0 +1,9 @@ +.app-loading { + position: absolute; + top: 50%; + left: 50%; + margin-top: -54px; + margin-left: -54px; + width: 108px !important; + height: 108px !important; +} \ No newline at end of file diff --git a/src/ui_ng/src/test.ts b/src/ui_ng/src/test.ts new file mode 100644 index 000000000..078f2e65c --- /dev/null +++ b/src/ui_ng/src/test.ts @@ -0,0 +1,45 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import './polyfills.ts'; + +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/proxy.js'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/jasmine-patch'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. +declare var __karma__: any; +declare var require: any; + +// Prevent Karma from running prematurely. +__karma__.loaded = function () {}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +let context = require.context('./', true, /\.spec\.ts/); +// And load the modules. +context.keys().map(context); +// Finally, start Karma to run the tests. +__karma__.start(); diff --git a/src/ui_ng/src/tsconfig.json b/src/ui_ng/src/tsconfig.json new file mode 100644 index 000000000..e67ce7c76 --- /dev/null +++ b/src/ui_ng/src/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "baseUrl": "", + "declaration": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es6", + "dom" + ], + "mapRoot": "./", + "module": "commonjs", + "moduleResolution": "node", + "outDir": "../dist/out-tsc", + "sourceMap": true, + "target": "es5", + "typeRoots": [ + "../node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/src/ui_ng/src/typings.d.ts b/src/ui_ng/src/typings.d.ts new file mode 100644 index 000000000..d859242cc --- /dev/null +++ b/src/ui_ng/src/typings.d.ts @@ -0,0 +1,15 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Typings reference file, you can add your own global typings here +// https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html diff --git a/src/ui_ng/tslint.json b/src/ui_ng/tslint.json new file mode 100644 index 000000000..ad0093e94 --- /dev/null +++ b/src/ui_ng/tslint.json @@ -0,0 +1,114 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "eofline": true, + "forin": true, + "indent": [ + true, + "spaces" + ], + "label-position": true, + "label-undefined": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-key": true, + "no-duplicate-variable": true, + "no-empty": false, + "no-eval": true, + "no-inferrable-types": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-unused-variable": true, + "no-unreachable": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + + "directive-selector-prefix": [true, "app"], + "component-selector-prefix": [true, "app"], + "directive-selector-name": [true, "camelCase"], + "component-selector-name": [true, "kebab-case"], + "directive-selector-type": [true, "attribute"], + "component-selector-type": [true, "element"], + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true, + "templates-use-public": true, + "invoke-injectable": true + } +} diff --git a/src/ui_ng/typings.json b/src/ui_ng/typings.json new file mode 100644 index 000000000..8628705b9 --- /dev/null +++ b/src/ui_ng/typings.json @@ -0,0 +1,5 @@ +{ + "globalDependencies": { + "es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504" + } +} diff --git a/src/ui_ng/yarn.lock b/src/ui_ng/yarn.lock new file mode 100644 index 000000000..a81a78a68 --- /dev/null +++ b/src/ui_ng/yarn.lock @@ -0,0 +1,6743 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@angular-cli/ast-tools@^1.0.1": + version "1.0.14" + resolved "https://registry.yarnpkg.com/@angular-cli/ast-tools/-/ast-tools-1.0.14.tgz#300f83dee68fbfd9b9d45c336597ab5bdf424d0d" + dependencies: + "@angular/tsc-wrapped" "^0.5.0" + denodeify "^1.2.1" + rxjs "^5.0.1" + typescript "~2.0.3" + +"@angular-cli/base-href-webpack@^1.0.0": + version "1.0.14" + resolved "https://registry.yarnpkg.com/@angular-cli/base-href-webpack/-/base-href-webpack-1.0.14.tgz#575f604f55f4c171608d3c377dc40d26858b49d6" + +"@angular/common@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-2.4.3.tgz#78d96bd2f8a1a105f635cd25e362ba5704b47f56" + +"@angular/compiler-cli@2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-2.2.3.tgz#d632bbf856802a20fc5589cb472f98fef5f011c4" + dependencies: + "@angular/tsc-wrapped" "^0.3.0" + minimist "^1.2.0" + reflect-metadata "^0.1.2" + +"@angular/compiler-cli@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-2.4.3.tgz#8339c2f428d29df3ea0eac17f08d296fc87eb160" + dependencies: + "@angular/tsc-wrapped" "0.5.1" + minimist "^1.2.0" + reflect-metadata "^0.1.2" + +"@angular/compiler@2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-2.2.3.tgz#5a96b8b6c620ffe0c3b87b2f254e588abeb77a12" + +"@angular/compiler@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-2.4.3.tgz#76a41916f90eda66643107740c4a9ae45cb7a6c1" + +"@angular/core@2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-2.2.3.tgz#18b09740bf51bdeb906153704e771bce53fecdf6" + +"@angular/core@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-2.4.3.tgz#a72a13bb9f01659b8388558cd6e3a570a8434b1c" + +"@angular/forms@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-2.4.3.tgz#0b4f59b8074e3d0fe987781e0f2af1ca1eec212b" + +"@angular/http@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/http/-/http-2.4.3.tgz#d8afca7362efb010600fde02360e01e848579be6" + +"@angular/platform-browser-dynamic@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-2.4.3.tgz#d65cc9bc4487e2a86e2c6f6641c711fccfa90c5a" + +"@angular/platform-browser@^2.4.1": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-2.4.3.tgz#eba8588d2fffc39d0b85a9180c297d9ca2f1f3a4" + +"@angular/router@^3.4.1": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-3.4.3.tgz#19846caa1f87d9b83f005f45a2be15b6744db8fd" + +"@angular/tsc-wrapped@0.5.1", "@angular/tsc-wrapped@^0.5.0": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-0.5.1.tgz#7a69bec999eef41903dddaaccdc862cfcface52c" + dependencies: + tsickle "^0.2" + +"@angular/tsc-wrapped@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-0.3.0.tgz#98cdeb5c38d145b187c0ad0397a8d98b217f33f2" + dependencies: + tsickle "^0.1.7" + +"@ngtools/webpack@^1.0.0": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.2.3.tgz#968e1994bf38115502df615b27654f7676d337a7" + dependencies: + enhanced-resolve "^2.3.0" + loader-utils "^0.2.16" + magic-string "^0.16.0" + source-map "^0.5.6" + +"@types/core-js@^0.9.34": + version "0.9.35" + resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-0.9.35.tgz#444064e63711cdcc62ea844d27642f6efc2285f2" + +"@types/jasmine@^2.2.30", "@types/jasmine@^2.2.31": + version "2.5.41" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.5.41.tgz#d5e86161a0af80d52062b310a33ed65b051a0713" + +"@types/node@^6.0.35", "@types/node@^6.0.42": + version "6.0.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.60.tgz#e7e134ebc674ae6ed93c36c767739b110d2c57fc" + +"@types/q@^0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.30.tgz#fd7ee1af40f21cdd1f519268073b3432ec17d7a3" + +"@types/selenium-webdriver@~2.53.30": + version "2.53.39" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.39.tgz#15ff93392c339abd39d6d3a04e715faa9a263cf3" + +"@webcomponents/custom-elements@1.0.0-alpha.3": + version "1.0.0-alpha.3" + resolved "https://registry.yarnpkg.com/@webcomponents/custom-elements/-/custom-elements-1.0.0-alpha.3.tgz#340d8cc8307730540ac92276c9a49149f6fc0116" + +abbrev@1, abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + +accepts@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.1.4.tgz#d71c96f7d41d0feda2c38cd14e8a27c04158df4a" + dependencies: + mime-types "~2.0.4" + negotiator "0.4.9" + +accepts@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" + dependencies: + mime-types "~2.1.11" + negotiator "0.6.1" + +acorn@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +adm-zip@0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.4.tgz#a61ed5ae6905c3aea58b3a657d25033091052736" + +adm-zip@0.4.7, adm-zip@^0.4.7: + version "0.4.7" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" + +after@0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.1.tgz#ab5d4fb883f596816d3515f8f791c0af486dd627" + +agent-base@2: + version "2.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.0.1.tgz#bd8f9e86a8eb221fffa07bd14befd55df142815e" + dependencies: + extend "~3.0.0" + semver "~5.0.1" + +ajv@^4.7.0: + version "4.10.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.10.4.tgz#c0974dd00b3464984892d6010aa9c2c945933254" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + +amdefine@1.0.0, amdefine@>=0.0.4: + version "1.0.0" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.0.tgz#fd17474700cb5cc9c2b709f0be9d23ce3c198c33" + +angular-cli@^1.0.0-beta.24: + version "1.0.0-beta.22-1" + resolved "https://registry.yarnpkg.com/angular-cli/-/angular-cli-1.0.0-beta.22-1.tgz#efea89b17dcbd74ca3bb7e4a6bbacabf565e610f" + dependencies: + "@angular-cli/ast-tools" "^1.0.1" + "@angular-cli/base-href-webpack" "^1.0.0" + "@angular/compiler" "2.2.3" + "@angular/compiler-cli" "2.2.3" + "@angular/core" "2.2.3" + "@ngtools/webpack" "^1.0.0" + angular2-template-loader "^0.5.0" + chalk "^1.1.3" + common-tags "^1.3.1" + compression-webpack-plugin "^0.3.2" + configstore "^2.0.0" + core-js "^2.4.0" + css-loader "^0.23.1" + debug "^2.1.3" + denodeify "^1.2.1" + diff "^2.2.2" + ember-cli-normalize-entity-name "^1.0.0" + ember-cli-preprocess-registry "^2.0.0" + ember-cli-string-utils "^1.0.0" + enhanced-resolve "^2.3.0" + exists-sync "0.0.3" + extract-text-webpack-plugin "^2.0.0-beta.4" + file-loader "^0.8.5" + findup "0.1.5" + fs-extra "^0.30.0" + get-caller-file "^1.0.0" + git-repo-info "^1.0.4" + glob "^7.0.3" + html-webpack-plugin "^2.19.0" + inflection "^1.7.0" + inquirer "^0.12.0" + is-git-url "^0.2.0" + isbinaryfile "^2.0.3" + istanbul-instrumenter-loader "^0.2.0" + json-loader "^0.5.4" + karma-sourcemap-loader "^0.3.7" + karma-webpack "^1.8.0" + leek "0.0.21" + less "^2.7.1" + less-loader "^2.2.3" + lodash "^4.11.1" + markdown-it "4.3.0" + markdown-it-terminal "0.0.3" + minimatch "^3.0.0" + mkdirp "^0.5.1" + node-modules-path "^1.0.0" + node-sass "^3.10.1" + nopt "^3.0.1" + npm-run-all "^3.0.0" + offline-plugin "^3.4.1" + opn "4.0.1" + ora "^0.2.0" + parse5 "^2.1.5" + portfinder "1.0.9" + postcss-loader "^0.9.1" + protractor "^3.3.0" + quick-temp "0.1.5" + raw-loader "^0.5.1" + readline2 "0.1.1" + reflect-metadata "^0.1.8" + remap-istanbul "^0.6.4" + resolve "^1.1.7" + rimraf "^2.5.3" + rsvp "^3.0.17" + rxjs "5.0.0-beta.12" + sass-loader "^3.2.0" + script-loader "^0.7.0" + semver "^5.1.0" + silent-error "^1.0.0" + source-map-loader "^0.1.5" + sourcemap-istanbul-instrumenter-loader "^0.2.0" + string-replace-loader "^1.0.3" + style-loader "^0.13.1" + stylus "^0.54.5" + stylus-loader "^2.1.0" + temp "0.8.3" + through "^2.3.6" + ts-loader "^0.8.2" + tslint "^3.15.1" + tslint-loader "^2.1.4" + typescript "~2.0.3" + url-loader "^0.5.7" + uuid "^3.0.0" + walk-sync "^0.2.6" + webpack "2.1.0-beta.25" + webpack-dev-server "2.1.0-beta.9" + webpack-md5-hash "0.0.5" + webpack-merge "^0.14.0" + yam "0.0.18" + zone.js "^0.6.23" + +angular2-template-loader@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/angular2-template-loader/-/angular2-template-loader-0.5.0.tgz#a16db190faaf9f8e8e9410f3c468370de75127a6" + dependencies: + codecov "^1.0.1" + loader-utils "^0.2.15" + +ansi-align@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-1.1.0.tgz#2f0c1658829739add5ebb15e6b0c6e3423f016ba" + dependencies: + string-width "^1.0.1" + +ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + +ansi-regex@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-1.1.1.tgz#41c847194646375e6a1a5d10c3ca054ef9fc980d" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.1.0, ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansicolors@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" + +any-promise@^1.0.0, any-promise@^1.1.0, any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +aproba@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.0.4.tgz#2713680775e7614c8ba186c065d4e2e52d1072c0" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + +are-we-there-yet@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.0 || ^1.1.13" + +argparse@^1.0.7, argparse@~1.0.2: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +argv@>=0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + +array-slice@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" + +array-to-error@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-to-error/-/array-to-error-1.1.1.tgz#d68812926d14097a205579a667eeaf1856a44c07" + dependencies: + array-to-sentence "^1.1.0" + +array-to-sentence@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-to-sentence/-/array-to-sentence-1.1.0.tgz#c804956dafa53232495b205a9452753a258d39fc" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1, array-uniq@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arraybuffer.slice@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asap@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" + +asn1.js@^4.0.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +async-disk-cache@^1.0.0: + version "1.0.9" + resolved "https://registry.yarnpkg.com/async-disk-cache/-/async-disk-cache-1.0.9.tgz#23bafb823184f463407e474e8d5f87899f72ca63" + dependencies: + debug "^2.1.3" + istextorbinary "2.1.0" + mkdirp "^0.5.0" + rimraf "^2.5.3" + rsvp "^3.0.18" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + +async@0.2.x, async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + +async@1.x, async@^1.3.0, async@^1.4.0, async@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^0.9.0, async@~0.9.0: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + +async@^2.0.1, async@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4" + dependencies: + lodash "^4.14.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +autoprefixer@^6.3.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.6.1.tgz#11a4077abb4b313253ec2f6e1adb91ad84253519" + dependencies: + browserslist "~1.5.1" + caniuse-db "^1.0.30000604" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^5.2.8" + postcss-value-parser "^3.2.3" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" + +babel-code-frame@^6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.20.0.tgz#b968f839090f9a8bc6d41938fb96cb84f7387b26" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^2.0.0" + +babel-runtime@^6.18.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.20.0.tgz#87300bdcf4cd770f09bf0048c64204e17806d16f" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + +balanced-match@^0.4.1, balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +base64-arraybuffer@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.2.tgz#474df4a9f2da24e05df3158c3b1db3c3cd46a154" + +base64-js@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + +base64id@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-0.1.0.tgz#02ce0fdeee0cef4f40080e1e73e834f0b1bfce3f" + +batch@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464" + +bcrypt-pbkdf@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" + dependencies: + tweetnacl "^0.14.3" + +beeper@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" + +benchmark@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-1.0.0.tgz#2f1e2fa4c359f11122aa183082218e957e390c73" + +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + dependencies: + callsite "1.0.0" + +big.js@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" + +binary-extensions@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + +"binaryextensions@1 || 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.0.0.tgz#e597d1a7a6a3558a2d1c7241a16c99965e6aa40f" + +bl@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.0.3.tgz#fc5421a28fd4226036c3b3891a66a25bc64d226e" + dependencies: + readable-stream "~2.0.5" + +blank-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/blank-object/-/blank-object-1.0.2.tgz#f990793fbe9a8c8dd013fb3219420bec81d5f4b9" + +blob@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.4.7: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + +body-parser@^1.12.4: + version "1.16.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.16.0.tgz#924a5e472c6229fb9d69b85a20d5f2532dec788b" + dependencies: + bytes "2.4.0" + content-type "~1.0.2" + debug "2.6.0" + depd "~1.1.0" + http-errors "~1.5.1" + iconv-lite "0.4.15" + on-finished "~2.3.0" + qs "6.2.1" + raw-body "~2.2.0" + type-is "~1.6.14" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +bootstrap@4.0.0-alpha.5: + version "4.0.0-alpha.5" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0-alpha.5.tgz#a126b648c3bd2f52b8fad4bbc5e2d0ad2abf7064" + dependencies: + jquery "1.9.1 - 3" + tether "^1.3.7" + +boxen@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-0.6.0.tgz#8364d4248ac34ff0ef1b2f2bf49a6c60ce0d81b6" + dependencies: + ansi-align "^1.1.0" + camelcase "^2.1.0" + chalk "^1.1.1" + cli-boxes "^1.0.0" + filled-array "^1.0.0" + object-assign "^4.0.1" + repeating "^2.0.0" + string-width "^1.0.1" + widest-line "^1.0.0" + +brace-expansion@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +braces@^0.1.2: + version "0.1.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6" + dependencies: + expand-range "^0.1.0" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +broccoli-clean-css@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/broccoli-clean-css/-/broccoli-clean-css-1.1.0.tgz#9db143d9af7e0ae79c26e3ac5a9bb2d720ea19fa" + dependencies: + broccoli-persistent-filter "^1.1.6" + clean-css-promise "^0.1.0" + inline-source-map-comment "^1.0.5" + json-stable-stringify "^1.0.0" + +broccoli-funnel@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/broccoli-funnel/-/broccoli-funnel-1.1.0.tgz#dfb91a37c902456456de4a40a1881948d65b27d9" + dependencies: + array-equal "^1.0.0" + blank-object "^1.0.1" + broccoli-plugin "^1.3.0" + debug "^2.2.0" + exists-sync "0.0.4" + fast-ordered-set "^1.0.0" + fs-tree-diff "^0.5.3" + heimdalljs "^0.2.0" + minimatch "^3.0.0" + mkdirp "^0.5.0" + path-posix "^1.0.0" + rimraf "^2.4.3" + symlink-or-copy "^1.0.0" + walk-sync "^0.3.1" + +broccoli-kitchen-sink-helpers@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/broccoli-kitchen-sink-helpers/-/broccoli-kitchen-sink-helpers-0.3.1.tgz#77c7c18194b9664163ec4fcee2793444926e0c06" + dependencies: + glob "^5.0.10" + mkdirp "^0.5.1" + +broccoli-merge-trees@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-1.2.1.tgz#16a7494ed56dbe61611f6c2d4817cfbaad2a3055" + dependencies: + broccoli-plugin "^1.3.0" + can-symlink "^1.0.0" + fast-ordered-set "^1.0.2" + fs-tree-diff "^0.5.4" + heimdalljs "^0.2.1" + heimdalljs-logger "^0.1.7" + rimraf "^2.4.3" + symlink-or-copy "^1.0.0" + +broccoli-persistent-filter@^1.1.6: + version "1.2.13" + resolved "https://registry.yarnpkg.com/broccoli-persistent-filter/-/broccoli-persistent-filter-1.2.13.tgz#61368669e2b8f35238fdd38a2a896597e4a1c821" + dependencies: + async-disk-cache "^1.0.0" + blank-object "^1.0.1" + broccoli-plugin "^1.0.0" + fs-tree-diff "^0.5.2" + hash-for-dep "^1.0.2" + heimdalljs "^0.2.1" + heimdalljs-logger "^0.1.7" + md5-hex "^1.0.2" + mkdirp "^0.5.1" + promise-map-series "^0.2.1" + rsvp "^3.0.18" + symlink-or-copy "^1.0.1" + walk-sync "^0.3.1" + +broccoli-plugin@^1.0.0, broccoli-plugin@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/broccoli-plugin/-/broccoli-plugin-1.3.0.tgz#bee704a8e42da08cb58e513aaa436efb7f0ef1ee" + dependencies: + promise-map-series "^0.2.1" + quick-temp "^0.1.3" + rimraf "^2.3.4" + symlink-or-copy "^1.1.8" + +brorand@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.6.tgz#4028706b915f91f7b349a2e0bf3c376039d216e5" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + dependencies: + buffer-xor "^1.0.2" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + inherits "^2.0.1" + +browserify-cipher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + dependencies: + pako "~0.2.0" + +browserslist@^1.0.1, browserslist@^1.5.2, browserslist@~1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.5.2.tgz#1c82fde0ee8693e6d15c49b7bff209dc06298c56" + dependencies: + caniuse-db "^1.0.30000604" + +buffer-shims@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +buffer-xor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +bytes@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070" + +bytes@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" + +callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^2.0.0, camelcase@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +can-symlink@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/can-symlink/-/can-symlink-1.0.0.tgz#97b607d8a84bb6c6e228b902d864ecb594b9d219" + dependencies: + tmp "0.0.28" + +caniuse-api@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.5.2.tgz#8f393c682f661c0a997b77bba6e826483fb3600e" + dependencies: + browserslist "^1.0.1" + caniuse-db "^1.0.30000346" + lodash.memoize "^4.1.0" + lodash.uniq "^4.3.0" + shelljs "^0.7.0" + +caniuse-db@^1.0.30000346, caniuse-db@^1.0.30000604: + version "1.0.30000613" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000613.tgz#639133b7a5380c1416f9701d23d54d093dd68299" + +capture-stack-trace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" + +cardinal@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-0.5.0.tgz#00d5f661dbd4aabfdf7d41ce48a5a59bca35a291" + dependencies: + ansicolors "~0.2.1" + redeyed "~0.5.0" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + +chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" + dependencies: + inherits "^2.0.1" + +clap@^1.0.9: + version "1.1.2" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.2.tgz#316545bf22229225a2cecaa6824cd2f56a9709ed" + dependencies: + chalk "^1.1.3" + +clarity-angular@^0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/clarity-angular/-/clarity-angular-0.8.2.tgz#224813b33e567c5ee7c8bc5a2b845b9bc2f34395" + +clarity-icons@^0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/clarity-icons/-/clarity-icons-0.8.2.tgz#908a7d8c9b4666d6799e74ec1db07f475d1efeb3" + +clarity-ui@^0.8.0: + version "0.8.2" + resolved "https://registry.yarnpkg.com/clarity-ui/-/clarity-ui-0.8.2.tgz#8e6e2c30c81caa6d0eb7940e72de1e99bfa6127c" + +clean-css-promise@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/clean-css-promise/-/clean-css-promise-0.1.1.tgz#43f3d2c8dfcb2bf071481252cd9b76433c08eecb" + dependencies: + array-to-error "^1.0.0" + clean-css "^3.4.5" + pinkie-promise "^2.0.0" + +clean-css@3.4.x, clean-css@^3.4.5: + version "3.4.23" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.23.tgz#604fbbca24c12feb59b02f00b84f1fb7ded6d001" + dependencies: + commander "2.8.x" + source-map "0.4.x" + +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + +cli-cursor@^1.0.1, cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + +cli-spinners@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" + +cli-table@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" + dependencies: + colors "1.0.3" + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + +cli-width@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +clone-stats@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + +clone@^1.0.0, clone@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +coa@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3" + dependencies: + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +codecov@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/codecov/-/codecov-1.0.1.tgz#97260ceac0e96b8eda8d562006558a53a139dffd" + dependencies: + argv ">=0.0.2" + execSync "1.0.2" + request ">=2.42.0" + urlgrey ">=0.4.0" + +codelyzer@~1.0.0-beta.3: + version "1.0.0-beta.4" + resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-1.0.0-beta.4.tgz#9b5e39222a024500a5bbbecfc95d6392ab851d03" + dependencies: + css-selector-tokenizer "^0.7.0" + cssauron "^1.4.0" + sprintf-js "^1.0.3" + +color-convert@^1.3.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" + dependencies: + color-name "^1.1.1" + +color-name@^1.0.0, color-name@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" + +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + +color@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +colormin@^1.0.5: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + dependencies: + color "^0.11.0" + css-color-names "0.0.4" + has "^1.0.1" + +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + +colors@1.1.2, colors@^1.0.3, colors@^1.1.0, colors@^1.1.2, colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +colors@~0.6.0-1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + +columnify@^1.5.2: + version "1.5.4" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + dependencies: + strip-ansi "^3.0.0" + wcwidth "^1.0.0" + +combine-lists@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6" + dependencies: + lodash "^4.5.0" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@2.8.x: + version "2.8.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commander@2.9.x, commander@^2.8.1, commander@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commander@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" + +common-tags@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" + dependencies: + babel-runtime "^6.18.0" + +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + +component-emitter@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" + +component-emitter@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.0.tgz#ccd113a86388d06482d03de3fc7df98526ba8efe" + +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + +compressible@~2.0.8: + version "2.0.9" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.9.tgz#6daab4e2b599c2770dd9e21e7a891b1c5a755425" + dependencies: + mime-db ">= 1.24.0 < 2" + +compression-webpack-plugin@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-0.3.2.tgz#1edfb0e749d7366d3e701670c463359b2c0cf704" + dependencies: + async "0.2.x" + webpack-sources "^0.1.0" + optionalDependencies: + node-zopfli "^2.0.0" + +compression@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.6.2.tgz#cceb121ecc9d09c52d7ad0c3350ea93ddd402bc3" + dependencies: + accepts "~1.3.3" + bytes "2.3.0" + compressible "~2.0.8" + debug "~2.2.0" + on-headers "~1.0.1" + vary "~1.1.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@1.5.0, concat-stream@^1.4.7: + version "1.5.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" + dependencies: + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" + +configstore@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1" + dependencies: + dot-prop "^3.0.0" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + object-assign "^4.0.1" + os-tmpdir "^1.0.0" + osenv "^0.1.0" + uuid "^2.0.1" + write-file-atomic "^1.1.2" + xdg-basedir "^2.0.0" + +connect-history-api-fallback@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" + +connect@^3.3.5: + version "3.5.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198" + dependencies: + debug "~2.2.0" + finalhandler "0.5.0" + parseurl "~1.3.1" + utils-merge "1.0.0" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +content-disposition@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" + +content-type@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-error-class@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + dependencies: + capture-stack-trace "^1.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^1.0.0" + sha.js "^2.3.6" + +create-hmac@^1.1.0, create-hmac@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" + dependencies: + create-hash "^1.1.0" + inherits "^2.0.1" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cross-spawn@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +crypto-browserify@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + +css-color-names@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + +css-loader@^0.23.1: + version "0.23.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.23.1.tgz#9fa23f2b5c0965235910ad5ecef3b8a36390fe50" + dependencies: + css-selector-tokenizer "^0.5.1" + cssnano ">=2.6.1 <4" + loader-utils "~0.2.2" + lodash.camelcase "^3.0.1" + object-assign "^4.0.1" + postcss "^5.0.6" + postcss-modules-extract-imports "^1.0.0" + postcss-modules-local-by-default "^1.0.1" + postcss-modules-scope "^1.0.0" + postcss-modules-values "^1.1.0" + source-list-map "^0.1.4" + +css-parse@1.7.x: + version "1.7.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b" + +css-select@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-selector-tokenizer@^0.5.1: + version "0.5.4" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.5.4.tgz#139bafd34a35fd0c1428487049e0699e6f6a2c21" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + +css-selector-tokenizer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css-selector-tokenizer@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + +cssauron@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8" + dependencies: + through X.X.X + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + +"cssnano@>=2.6.1 <4": + version "3.10.0" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" + dependencies: + autoprefixer "^6.3.1" + decamelize "^1.1.2" + defined "^1.0.0" + has "^1.0.1" + object-assign "^4.0.1" + postcss "^5.0.14" + postcss-calc "^5.2.0" + postcss-colormin "^2.1.8" + postcss-convert-values "^2.3.4" + postcss-discard-comments "^2.0.4" + postcss-discard-duplicates "^2.0.1" + postcss-discard-empty "^2.0.1" + postcss-discard-overridden "^0.1.1" + postcss-discard-unused "^2.2.1" + postcss-filter-plugins "^2.0.0" + postcss-merge-idents "^2.1.5" + postcss-merge-longhand "^2.0.1" + postcss-merge-rules "^2.0.3" + postcss-minify-font-values "^1.0.2" + postcss-minify-gradients "^1.0.1" + postcss-minify-params "^1.0.4" + postcss-minify-selectors "^2.0.4" + postcss-normalize-charset "^1.1.0" + postcss-normalize-url "^3.0.7" + postcss-ordered-values "^2.1.0" + postcss-reduce-idents "^2.2.2" + postcss-reduce-initial "^1.0.0" + postcss-reduce-transforms "^1.0.3" + postcss-svgo "^2.1.1" + postcss-unique-selectors "^2.0.2" + postcss-value-parser "^3.2.3" + postcss-zindex "^2.0.1" + +csso@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/csso/-/csso-2.2.1.tgz#51fbb5347e50e81e6ed51668a48490ae6fe2afe2" + dependencies: + clap "^1.0.9" + source-map "^0.5.3" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +dateformat@^1.0.11: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + +debug@*, debug@2, debug@2.6.0, debug@^2.1.0, debug@^2.1.3, debug@^2.2.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" + dependencies: + ms "0.7.2" + +debug@0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + +debug@2.2.0, debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@^0.4.0, deep-extend@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +defaults@^1.0.2, defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +del@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +denodeify@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" + +depd@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + +diff@^2.1.1, diff@^2.2.1, diff@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99" + +diff@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" + +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dom-converter@~0.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b" + dependencies: + utila "~0.3" + +dom-serialize@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domain-browser@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + +domelementtype@1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594" + dependencies: + domelementtype "1" + +domutils@1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" + dependencies: + is-obj "^1.0.0" + +duplexer2@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + dependencies: + readable-stream "~1.1.9" + +duplexer2@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + dependencies: + readable-stream "^2.0.2" + +duplexer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +editions@^1.1.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.3.tgz#0907101bdda20fac3cbe334c27cbd0688dc99a5b" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +ejs@^2.3.4: + version "2.5.5" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.5.tgz#6ef4e954ea7dcf54f66aad2fe7aa421932d9ed77" + +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + +elliptic@^6.0.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.2.tgz#e4c81e0829cf0a65ab70e998b8232723b5c1bc48" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" + +ember-cli-normalize-entity-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ember-cli-normalize-entity-name/-/ember-cli-normalize-entity-name-1.0.0.tgz#0b14f7bcbc599aa117b5fddc81e4fd03c4bad5b7" + dependencies: + silent-error "^1.0.0" + +ember-cli-preprocess-registry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ember-cli-preprocess-registry/-/ember-cli-preprocess-registry-2.0.0.tgz#45c8b985eba06bb443b3abce1c3c6220fdcb8094" + dependencies: + broccoli-clean-css "^1.1.0" + broccoli-funnel "^1.0.0" + broccoli-merge-trees "^1.0.0" + debug "^2.2.0" + exists-sync "0.0.3" + lodash "^3.10.0" + process-relative-require "^1.0.0" + silent-error "^1.0.0" + +ember-cli-string-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ember-cli-string-utils/-/ember-cli-string-utils-1.0.0.tgz#d07b17d0b6223c42e09bfb835ee2b8466ec9b88e" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + +engine.io-client@1.6.9: + version "1.6.9" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.6.9.tgz#1d6ad48048a5083c95096943b29d36efdb212401" + dependencies: + component-emitter "1.1.2" + component-inherit "0.0.3" + debug "2.2.0" + engine.io-parser "1.2.4" + has-cors "1.1.0" + indexof "0.0.1" + parsejson "0.0.1" + parseqs "0.0.2" + parseuri "0.0.4" + ws "1.0.1" + xmlhttprequest-ssl "1.5.1" + yeast "0.1.2" + +engine.io-parser@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.2.4.tgz#e0897b0bf14e792d4cd2a5950553919c56948c42" + dependencies: + after "0.8.1" + arraybuffer.slice "0.0.6" + base64-arraybuffer "0.1.2" + blob "0.0.4" + has-binary "0.1.6" + utf8 "2.1.0" + +engine.io@1.6.10: + version "1.6.10" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.6.10.tgz#f87d84e1bd21d1a2ec7f8deef0c62054acdfb27a" + dependencies: + accepts "1.1.4" + base64id "0.1.0" + debug "2.2.0" + engine.io-parser "1.2.4" + ws "1.0.1" + +enhanced-resolve@^0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.2.0" + tapable "^0.1.8" + +enhanced-resolve@^2.2.0, enhanced-resolve@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-2.3.0.tgz#a115c32504b6302e85a76269d7a57ccdd962e359" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.3.0" + object-assign "^4.0.1" + tapable "^0.2.3" + +enhanced-resolve@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.0.3.tgz#df14c06b5fc5eecade1094c9c5a12b4b3edc0b62" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.5" + +ensure-posix-path@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ensure-posix-path/-/ensure-posix-path-1.0.2.tgz#a65b3e42d0b71cfc585eb774f9943c8d9b91b0c2" + +ent@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + +entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +errno@^0.1.1, errno@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +error-ex@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.4.3: + version "1.6.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.6.1.tgz#bb8a2064120abcf928a086ea3d9043114285ec99" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.0" + is-callable "^1.1.3" + is-regex "^1.0.3" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +es6-promise@^3.0.2: + version "3.3.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + +es6-promise@~4.0.3: + version "4.0.5" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + +esprima-fb@~12001.1.0-dev-harmony-fb: + version "12001.1.0-dev-harmony-fb" + resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-12001.1.0-dev-harmony-fb.tgz#d84400384ba95ce2678c617ad24a7f40808da915" + +esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" + +event-stream@~3.3.0: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter3@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +eventsource@~0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" + dependencies: + original ">=0.0.5" + +evp_bytestokey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" + dependencies: + create-hash "^1.1.1" + +execSync@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/execSync/-/execSync-1.0.2.tgz#1f42eda582225180053224ecdd3fd1960fdb3139" + dependencies: + temp "~0.5.1" + +exists-sync@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/exists-sync/-/exists-sync-0.0.3.tgz#b910000bedbb113b378b82f5f5a7638107622dcf" + +exists-sync@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/exists-sync/-/exists-sync-0.0.4.tgz#9744c2c428cc03b01060db454d4b12f0ef3c8879" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + +expand-braces@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea" + dependencies: + array-slice "^0.2.3" + array-unique "^0.2.1" + braces "^0.1.2" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" + dependencies: + is-number "^0.1.1" + repeat-string "^0.2.2" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +express@^4.13.3: + version "4.14.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.14.0.tgz#c1ee3f42cdc891fb3dc650a8922d51ec847d0d66" + dependencies: + accepts "~1.3.3" + array-flatten "1.1.1" + content-disposition "0.5.1" + content-type "~1.0.2" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "~2.2.0" + depd "~1.1.0" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + finalhandler "0.5.0" + fresh "0.3.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.1" + path-to-regexp "0.1.7" + proxy-addr "~1.1.2" + qs "6.2.0" + range-parser "~1.2.0" + send "0.14.1" + serve-static "~1.11.1" + type-is "~1.6.13" + utils-merge "1.0.0" + vary "~1.1.0" + +extend@3, extend@^3.0.0, extend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extract-text-webpack-plugin@^2.0.0-beta.4: + version "2.0.0-beta.5" + resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.0.0-beta.5.tgz#595e32ae2466ad2129a928328485fcc064ce57f0" + dependencies: + async "^2.1.2" + loader-utils "^0.2.3" + webpack-sources "^0.1.0" + +extract-zip@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d8688a6c4" + dependencies: + concat-stream "1.5.0" + debug "0.7.4" + mkdirp "0.5.0" + yauzl "2.4.1" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +fancy-log@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.0.tgz#45be17d02bb9917d60ccffd4995c999e6c8c9948" + dependencies: + chalk "^1.1.1" + time-stamp "^1.0.0" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fast-ordered-set@^1.0.0, fast-ordered-set@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-ordered-set/-/fast-ordered-set-1.0.3.tgz#3fbb36634f7be79e4f7edbdb4a357dee25d184eb" + dependencies: + blank-object "^1.0.1" + +fastparse@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.0.tgz#d9ccf0e789e7db725d74bc4877d23aa42972ac50" + dependencies: + websocket-driver ">=0.5.1" + +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + dependencies: + pend "~1.2.0" + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +file-loader@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.8.5.tgz#9275d031fe780f27d47f5f4af02bd43713cc151b" + dependencies: + loader-utils "~0.2.5" + +filename-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + +fileset@0.2.x: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-0.2.1.tgz#588ef8973c6623b2a76df465105696b96aac8067" + dependencies: + glob "5.x" + minimatch "2.x" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +filled-array@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filled-array/-/filled-array-1.1.0.tgz#c3c4f6c663b923459a9aa29912d2d031f1507f84" + +finalhandler@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" + dependencies: + debug "~2.2.0" + escape-html "~1.0.3" + on-finished "~2.3.0" + statuses "~1.3.0" + unpipe "~1.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +findup-sync@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" + dependencies: + glob "~5.0.0" + +findup@0.1.5, findup@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/findup/-/findup-0.1.5.tgz#8ad929a3393bac627957a7e5de4623b06b0e2ceb" + dependencies: + colors "~0.6.0-1" + commander "~2.1.0" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + +for-in@^0.1.5: + version "0.1.6" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" + +for-own@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" + dependencies: + for-in "^0.1.5" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@^2.0.0, form-data@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~1.0.0-rc3: + version "1.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.1.tgz#ae315db9a4907fa065502304a66d7733475ee37c" + dependencies: + async "^2.0.1" + combined-stream "^1.0.5" + mime-types "^2.1.11" + +forwarded@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" + +fresh@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" + +from@~0: + version "0.1.3" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.3.tgz#ef63ac2062ac32acf7862e0d40b44b896f22f3bc" + +fs-extra@^0.16.3: + version "0.16.5" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.16.5.tgz#1ad661fa6c86c9608cd1b49efc6fce834939a750" + dependencies: + graceful-fs "^3.0.5" + jsonfile "^2.0.0" + rimraf "^2.2.8" + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + +fs-tree-diff@^0.5.2, fs-tree-diff@^0.5.3, fs-tree-diff@^0.5.4: + version "0.5.6" + resolved "https://registry.yarnpkg.com/fs-tree-diff/-/fs-tree-diff-0.5.6.tgz#342665749e8dca406800b672268c8f5073f3e623" + dependencies: + heimdalljs-logger "^0.1.7" + object-assign "^4.1.0" + path-posix "^1.0.0" + symlink-or-copy "^1.1.8" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.0.17" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.17.tgz#8537f3f12272678765b4fd6528c0f1f66f8f4558" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fstream-ignore@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.0.2, function-bind@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" + +gauge@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.2.tgz#15cecc31b02d05345a5d6b0e171cdb3ad2307774" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + supports-color "^0.2.0" + wide-align "^1.1.0" + +gaze@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" + dependencies: + globule "^1.0.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +get-caller-file@^1.0.0, get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +getpass@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + dependencies: + assert-plus "^1.0.0" + +git-repo-info@^1.0.4: + version "1.4.0" + resolved "https://registry.yarnpkg.com/git-repo-info/-/git-repo-info-1.4.0.tgz#ed210221defd3fdefce8b16ac61985cabe242e4a" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@5.x, glob@^5.0.10, glob@^5.0.15, glob@~5.0.0: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.0.x: + version "7.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + dependencies: + inherits "2" + minimatch "0.3" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@~7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~6.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globule@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.1.0.tgz#c49352e4dc183d85893ee825385eb994bb6df45f" + dependencies: + glob "~7.1.1" + lodash "~4.16.4" + minimatch "~3.0.2" + +glogg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" + dependencies: + sparkles "^1.0.0" + +got@^5.0.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35" + dependencies: + create-error-class "^3.0.1" + duplexer2 "^0.1.4" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + node-status-codes "^1.0.0" + object-assign "^4.0.1" + parse-json "^2.1.0" + pinkie-promise "^2.0.0" + read-all-stream "^3.0.0" + readable-stream "^2.0.5" + timed-out "^3.0.0" + unzip-response "^1.0.2" + url-parse-lax "^1.0.0" + +graceful-fs@^3.0.5: + version "3.0.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" + dependencies: + natives "^1.1.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +graceful-fs@~1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +gulp-util@3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.7.tgz#78925c4b8f8b49005ac01a011c557e6218941cbb" + dependencies: + array-differ "^1.0.0" + array-uniq "^1.0.2" + beeper "^1.0.0" + chalk "^1.0.0" + dateformat "^1.0.11" + fancy-log "^1.1.0" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + lodash._reescape "^3.0.0" + lodash._reevaluate "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.template "^3.0.0" + minimist "^1.1.0" + multipipe "^0.1.2" + object-assign "^3.0.0" + replace-ext "0.0.1" + through2 "^2.0.0" + vinyl "^0.5.0" + +gulplog@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + dependencies: + glogg "^1.0.0" + +handle-thing@^1.2.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" + +handlebars@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + +har-validator@~2.0.2, har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-binary@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.6.tgz#25326f39cfa4f616ad8787894e3af2cfbc7b6e10" + dependencies: + isarray "0.0.1" + +has-binary@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c" + dependencies: + isarray "0.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-gulplog@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" + dependencies: + sparkles "^1.0.0" + +has-unicode@^2.0.0, has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hash-for-dep@^1.0.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/hash-for-dep/-/hash-for-dep-1.1.2.tgz#e3347ed92960eb0bb53a2c6c2b70e36d75b7cd0c" + dependencies: + broccoli-kitchen-sink-helpers "^0.3.1" + heimdalljs "^0.2.3" + heimdalljs-logger "^0.1.7" + resolve "^1.1.6" + +hash.js@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" + dependencies: + inherits "^2.0.1" + +hasha@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" + dependencies: + is-stream "^1.0.1" + pinkie-promise "^2.0.0" + +hawk@~3.1.0, hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +he@1.1.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.0.tgz#29319d49beec13a9b1f3c4f9b2a6dde4859bb2a7" + +heimdalljs-logger@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/heimdalljs-logger/-/heimdalljs-logger-0.1.7.tgz#10e340af5c22a811e34522d9b9397675ad589ca4" + dependencies: + debug "^2.2.0" + heimdalljs "^0.2.0" + +heimdalljs@^0.2.0, heimdalljs@^0.2.1, heimdalljs@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/heimdalljs/-/heimdalljs-0.2.3.tgz#35b82a6a4d73541fc4fb88d2fe2b23608fb4f779" + dependencies: + rsvp "~3.2.1" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hosted-git-info@^2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-comment-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" + +html-minifier@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.2.3.tgz#d2ff536e24d95726c332493d8f77d84dbed85372" + dependencies: + camel-case "3.0.x" + clean-css "3.4.x" + commander "2.9.x" + he "1.1.x" + ncname "1.0.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "2.7.x" + +html-webpack-plugin@^2.19.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.26.0.tgz#ba97c8a66f912b85df80d2aeea65966c8bd9249e" + dependencies: + bluebird "^3.4.7" + html-minifier "^3.2.3" + loader-utils "^0.2.16" + lodash "^4.17.3" + pretty-error "^2.0.2" + toposort "^1.0.0" + +htmlparser2@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" + dependencies: + domelementtype "1" + domhandler "2.1" + domutils "1.1" + readable-stream "1.0" + +http-deceiver@^1.2.4: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + +http-errors@~1.5.0, http-errors@~1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750" + dependencies: + inherits "2.0.3" + setprototypeof "1.0.2" + statuses ">= 1.3.1 < 2" + +http-proxy-agent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" + dependencies: + agent-base "2" + debug "2" + extend "3" + +http-proxy-middleware@~0.17.1: + version "0.17.3" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.3.tgz#940382147149b856084f5534752d5b5a8168cd1d" + dependencies: + http-proxy "^1.16.2" + is-glob "^3.1.0" + lodash "^4.17.2" + micromatch "^2.3.11" + +http-proxy@^1.13.0, http-proxy@^1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" + dependencies: + eventemitter3 "1.x.x" + requires-port "1.x.x" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + +https-proxy-agent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" + dependencies: + agent-base "2" + debug "2" + extend "3" + +iconv-lite@0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + +icss-replace-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5" + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + +image-size@~0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.1.tgz#28eea8548a4b1443480ddddc1e083ae54652439f" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflection@^1.7.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@^1.3.4, ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +inline-source-map-comment@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/inline-source-map-comment/-/inline-source-map-comment-1.0.5.tgz#50a8a44c2a790dfac441b5c94eccd5462635faf6" + dependencies: + chalk "^1.0.0" + get-stdin "^4.0.1" + minimist "^1.1.1" + sum-up "^1.0.1" + xtend "^4.0.0" + +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + dependencies: + ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + figures "^1.3.5" + lodash "^4.3.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +interpret@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" + +invariant@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ipaddr.js@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.2.0.tgz#8aba49c9192799585bdd643e0ccb50e8ae777ba4" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + +is-absolute@^0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" + dependencies: + is-relative "^0.2.1" + is-windows "^0.2.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2, is-buffer@~1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-dotfile@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-git-url@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/is-git-url/-/is-git-url-0.2.3.tgz#445200d6fbd6da028fb5e01440d9afc93f3ccb64" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-my-json-valid@^2.12.4: + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-npm@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + +is-number@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + +is-regex@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.3.tgz#0d55182bddf9f2fde278220aec3a75642c908637" + +is-relative@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" + dependencies: + is-unc-path "^0.1.1" + +is-retry-allowed@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + +is-stream@^1.0.0, is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-svg@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-unc-path@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" + dependencies: + unc-path-regex "^0.1.0" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-windows@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isbinaryfile@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-2.0.4.tgz#d23592e6a6f093efb84c2e6152056be294e414a1" + +isbinaryfile@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" + +isexe@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" + +isobject@^2.0.0, isobject@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +istanbul-instrumenter-loader@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-0.2.0.tgz#643e4e5e4e8f9466863a29a977d283ab372c019c" + dependencies: + istanbul "0.x.x" + loader-utils "0.x.x" + object-assign "4.x.x" + +istanbul@0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.3.tgz#5b714ee0ae493ac5ef204b99f3872bceef73d53a" + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + fileset "0.2.x" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + +istanbul@0.x.x, istanbul@^0.4.3: + version "0.4.5" + resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + +istextorbinary@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.1.0.tgz#dbed2a6f51be2f7475b68f89465811141b758874" + dependencies: + binaryextensions "1 || 2" + editions "^1.1.1" + textextensions "1 || 2" + +jasmine-core@2.4.1, jasmine-core@~2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.4.1.tgz#6f83ab3a0f16951722ce07d206c773d57cc838be" + +jasmine-core@~2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297" + +jasmine-spec-reporter@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-2.5.0.tgz#12e4782e6445c0dfabb8def4c52edd30f6362d42" + dependencies: + colors "1.1.2" + +jasmine@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.4.1.tgz#9016dda453213d27ac6d43dc4ea97315a189085e" + dependencies: + exit "^0.1.2" + glob "^3.2.11" + jasmine-core "~2.4.0" + +jasmine@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.5.2.tgz#6283cef7085c095cc25d651e954df004f7e3e421" + dependencies: + exit "^0.1.2" + glob "^7.0.6" + jasmine-core "~2.5.2" + +jasminewd2@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-0.0.10.tgz#94f48ae2bc946cad643035467b4bb7ea9c1075ef" + +jasminewd2@0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-0.0.9.tgz#d6be40841d440dbe1ceee5a078de62683b0e56a7" + +jodid25519@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" + dependencies: + jsbn "~0.1.0" + +"jquery@1.9.1 - 3": + version "3.1.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.1.1.tgz#347c1c21c7e004115e0a4da32cece041fad3c8a3" + +js-base64@^2.1.9: + version "2.1.9" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce" + +js-tokens@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" + +js-tokens@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.0.tgz#a2f2a969caae142fb3cd56228358c89366957bd1" + +js-yaml@3.x: + version "3.7.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +js-yaml@~3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsbn@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-loader@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json3@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.2.6.tgz#f6efc93c06a04de9aec53053df2559bb19e2038b" + +json3@3.3.2, json3@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonfile@^2.0.0, jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" + dependencies: + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +karma-cli@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/karma-cli/-/karma-cli-1.0.1.tgz#ae6c3c58a313a1d00b45164c455b9b86ce17f960" + dependencies: + resolve "^1.1.6" + +karma-jasmine@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf" + +karma-mocha-reporter@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.1.tgz#8508b2f0925433a6417f0c528e53fcb411745758" + dependencies: + chalk "1.1.3" + +karma-phantomjs-launcher@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.2.tgz#19e1041498fd75563ed86730a22c1fe579fa8fb1" + dependencies: + lodash "^4.0.1" + phantomjs-prebuilt "^2.1.7" + +karma-remap-istanbul@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/karma-remap-istanbul/-/karma-remap-istanbul-0.2.2.tgz#1cdf6c85a55c6b20e9c7149c0a8c55533038d9d9" + dependencies: + istanbul "^0.4.3" + remap-istanbul "^0.6.4" + +karma-sourcemap-loader@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13d46fed062b042e1009d4c4505d8" + dependencies: + graceful-fs "^4.1.2" + +karma-webpack@^1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-1.8.1.tgz#39d5fd2edeea3cc3ef5b405989b37d5b0e6a3b4e" + dependencies: + async "~0.9.0" + loader-utils "^0.2.5" + lodash "^3.8.0" + source-map "^0.1.41" + webpack-dev-middleware "^1.0.11" + +karma@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-1.2.0.tgz#6dca89ec25f4753f120f834c89398098040fd63e" + dependencies: + bluebird "^3.3.0" + body-parser "^1.12.4" + chokidar "^1.4.1" + colors "^1.1.0" + combine-lists "^1.0.0" + connect "^3.3.5" + core-js "^2.2.0" + di "^0.0.1" + dom-serialize "^2.2.0" + expand-braces "^0.1.1" + glob "^7.0.3" + graceful-fs "^4.1.2" + http-proxy "^1.13.0" + isbinaryfile "^3.0.0" + lodash "^3.8.0" + log4js "^0.6.31" + mime "^1.3.4" + minimatch "^3.0.0" + optimist "^0.6.1" + qjobs "^1.1.4" + rimraf "^2.3.3" + socket.io "1.4.7" + source-map "^0.5.3" + tmp "0.0.28" + useragent "^2.1.9" + +kew@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" + +kind-of@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" + dependencies: + is-buffer "^1.0.2" + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + optionalDependencies: + graceful-fs "^4.1.9" + +latest-version@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-2.0.0.tgz#56f8d6139620847b8017f8f1f4d78e211324168b" + dependencies: + package-json "^2.0.0" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lazy-req@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/lazy-req/-/lazy-req-1.1.0.tgz#bdaebead30f8d824039ce0ce149d4daa07ba1fac" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +leek@0.0.21: + version "0.0.21" + resolved "https://registry.yarnpkg.com/leek/-/leek-0.0.21.tgz#09804bf70f8aefbba745f5d56d2a4debf22711ff" + dependencies: + debug "^2.1.0" + lodash.assign "^3.2.0" + request "^2.27.0" + rsvp "^3.0.21" + +less-loader@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-2.2.3.tgz#b6d8f8139c8493df09d992a93a00734b08f84528" + dependencies: + loader-utils "^0.2.5" + +less@^2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/less/-/less-2.7.2.tgz#368d6cc73e1fb03981183280918743c5dcf9b3df" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + mime "^1.2.11" + mkdirp "^0.5.0" + promise "^7.1.1" + request "^2.72.0" + source-map "^0.5.3" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +linkify-it@~1.2.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-1.2.4.tgz#0773526c317c8fd13bd534ee1d180ff88abf881a" + dependencies: + uc.micro "^1.0.1" + +listify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/listify/-/listify-1.0.0.tgz#03ca7ba2d150d4267773f74e57558d1053d2bee3" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loader-runner@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.2.0.tgz#824c1b699c4e7a2b6501b85902d5b862bf45b3fa" + +loader-utils@0.2.x, loader-utils@0.x.x, loader-utils@^0.2.11, loader-utils@^0.2.14, loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.3, loader-utils@^0.2.5, loader-utils@^0.2.6, loader-utils@^0.2.7, loader-utils@^0.2.9, loader-utils@~0.2.2, loader-utils@~0.2.5: + version "0.2.16" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +lockfile@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.3.tgz#2638fc39a0331e9cac1a04b71799931c9c50df79" + +lodash._arraycopy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" + +lodash._arrayeach@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e" + +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basecallback@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/lodash._basecallback/-/lodash._basecallback-3.3.1.tgz#b7b2bb43dc2160424a21ccf26c57e443772a8e27" + dependencies: + lodash._baseisequal "^3.0.0" + lodash._bindcallback "^3.0.0" + lodash.isarray "^3.0.0" + lodash.pairs "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + +lodash._baseeach@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash._baseeach/-/lodash._baseeach-3.0.4.tgz#cf8706572ca144e8d9d75227c990da982f932af3" + dependencies: + lodash.keys "^3.0.0" + +lodash._basefind@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basefind/-/lodash._basefind-3.0.0.tgz#b2bba05cc645f972de2cf925fa2bf63a0f60c8ae" + +lodash._basefindindex@^3.0.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/lodash._basefindindex/-/lodash._basefindindex-3.6.0.tgz#f083360a1b022418ed81bc899beb312e21e74a4f" + +lodash._basefor@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" + +lodash._baseisequal@^3.0.0: + version "3.0.7" + resolved "https://registry.yarnpkg.com/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz#d8025f76339d29342767dcc887ce5cb95a5b51f1" + dependencies: + lodash.isarray "^3.0.0" + lodash.istypedarray "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basetostring@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" + +lodash._basevalues@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" + +lodash._bindcallback@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" + +lodash._createassigner@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" + dependencies: + lodash._bindcallback "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash.restparam "^3.0.0" + +lodash._createcompounder@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz#5dd2cb55372d6e70e0e2392fb2304d6631091075" + dependencies: + lodash.deburr "^3.0.0" + lodash.words "^3.0.0" + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + +lodash._reescape@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" + +lodash._reevaluate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + +lodash._root@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + +lodash.assign@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" + dependencies: + lodash._baseassign "^3.0.0" + lodash._createassigner "^3.0.0" + lodash.keys "^3.0.0" + +lodash.assign@^4.0.3, lodash.assign@^4.0.6, lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + +lodash.camelcase@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz#932c8b87f8a4377897c67197533282f97aeac298" + dependencies: + lodash._createcompounder "^3.0.0" + +lodash.clonedeep@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.deburr@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz#6da8f54334a366a7cf4c4c76ef8d80aa1b365ed5" + dependencies: + lodash._root "^3.0.0" + +lodash.escape@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" + dependencies: + lodash._root "^3.0.0" + +lodash.find@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-3.2.1.tgz#046e319f3ace912ac6c9246c7f683c5ec07b36ad" + dependencies: + lodash._basecallback "^3.0.0" + lodash._baseeach "^3.0.0" + lodash._basefind "^3.0.0" + lodash._basefindindex "^3.0.0" + lodash.isarray "^3.0.0" + lodash.keys "^3.0.0" + +lodash.indexof@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/lodash.indexof/-/lodash.indexof-4.0.5.tgz#53714adc2cddd6ed87638f893aa9b6c24e31ef3c" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.isequal@^4.2.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + +lodash.isplainobject@^3.0.0, lodash.isplainobject@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz#9a8238ae16b200432960cd7346512d0123fbf4c5" + dependencies: + lodash._basefor "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.keysin "^3.0.0" + +lodash.istypedarray@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz#c9a477498607501d8e8494d283b87c39281cef62" + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.keysin@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f" + dependencies: + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.memoize@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + +lodash.merge@^3.0.2, lodash.merge@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-3.3.2.tgz#0d90d93ed637b1878437bb3e21601260d7afe994" + dependencies: + lodash._arraycopy "^3.0.0" + lodash._arrayeach "^3.0.0" + lodash._createassigner "^3.0.0" + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + lodash.isplainobject "^3.0.0" + lodash.istypedarray "^3.0.0" + lodash.keys "^3.0.0" + lodash.keysin "^3.0.0" + lodash.toplainobject "^3.0.0" + +lodash.pairs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.pairs/-/lodash.pairs-3.0.1.tgz#bbe08d5786eeeaa09a15c91ebf0dcb7d2be326a9" + dependencies: + lodash.keys "^3.0.0" + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + +lodash.template@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" + dependencies: + lodash._basecopy "^3.0.0" + lodash._basetostring "^3.0.0" + lodash._basevalues "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + lodash.keys "^3.0.0" + lodash.restparam "^3.0.0" + lodash.templatesettings "^3.0.0" + +lodash.templatesettings@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + +lodash.toplainobject@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash.toplainobject/-/lodash.toplainobject-3.0.0.tgz#28790ad942d293d78aa663a07ecf7f52ca04198d" + dependencies: + lodash._basecopy "^3.0.0" + lodash.keysin "^3.0.0" + +lodash.uniq@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash.words@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-3.2.0.tgz#4e2a8649bc08745b17c695b1a3ce8fee596623b3" + dependencies: + lodash._root "^3.0.0" + +lodash@^3.10.0, lodash@^3.10.1, lodash@^3.8.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.3.0, lodash@^4.5.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +lodash@~4.16.4: + version "4.16.6" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" + +log-update@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" + dependencies: + ansi-escapes "^1.0.0" + cli-cursor "^1.0.2" + +log4js@^0.6.31: + version "0.6.38" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" + dependencies: + readable-stream "~1.0.2" + semver "~4.3.3" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.3.tgz#c92393d976793eee5ba4edb583cf8eae35bd9bfb" + +lowercase-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +lru-cache@2.2.x: + version "2.2.4" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" + +lru-cache@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +macaddress@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" + +magic-string@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.16.0.tgz#970ebb0da7193301285fb1aa650f39bdd81eb45a" + dependencies: + vlq "^0.2.1" + +make-error-cause@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" + dependencies: + make-error "^1.2.0" + +make-error@^1.1.1, make-error@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.2.1.tgz#9a6dfb4844423b9f145806728d05c6e935670e75" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + +markdown-it-terminal@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/markdown-it-terminal/-/markdown-it-terminal-0.0.3.tgz#c77a8533c2170b46d2a907a3c3452d4d7f4aa5db" + dependencies: + ansi-styles "^2.1.0" + cardinal "^0.5.0" + cli-table "^0.3.1" + lodash.merge "^3.3.2" + markdown-it "^4.4.0" + +markdown-it@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-4.3.0.tgz#0ee2b0724079d186b3f04b7345ce395ae47cc474" + dependencies: + argparse "~1.0.2" + entities "~1.1.1" + linkify-it "~1.2.0" + mdurl "~1.0.0" + uc.micro "^1.0.0" + +markdown-it@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-4.4.0.tgz#3df373dbea587a9a7fef3e56311b68908f75c414" + dependencies: + argparse "~1.0.2" + entities "~1.1.1" + linkify-it "~1.2.0" + mdurl "~1.0.0" + uc.micro "^1.0.0" + +matcher-collection@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-1.0.4.tgz#2f66ae0869996f29e43d0b62c83dd1d43e581755" + dependencies: + minimatch "^3.0.2" + +math-expression-evaluator@^1.2.14: + version "1.2.14" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.14.tgz#39511771ed9602405fba9affff17eb4d2a3843ab" + dependencies: + lodash.indexof "^4.0.5" + +md5-hex@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-1.3.0.tgz#d2c4afe983c4370662179b8cad145219135046c4" + dependencies: + md5-o-matic "^0.1.1" + +md5-o-matic@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3" + +md5@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + +mdurl@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +memory-fs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" + +memory-fs@^0.3.0, memory-fs@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.3.0, meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^2.1.5, micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +miller-rabin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +"mime-db@>= 1.24.0 < 2", mime-db@~1.26.0: + version "1.26.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" + +mime-db@~1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.12.0.tgz#3d0c63180f458eb10d325aaa37d7c58ae312e9d7" + +mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7: + version "2.1.14" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" + dependencies: + mime-db "~1.26.0" + +mime-types@~2.0.4: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.0.14.tgz#310e159db23e077f8bb22b748dabfa4957140aa6" + dependencies: + mime-db "~1.12.0" + +mime@1.2.x: + version "1.2.11" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" + +mime@1.3.4, mime@^1.2.11, mime@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + +minimalistic-assert@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + +minimatch@2.x: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + dependencies: + brace-expansion "^1.0.0" + +minimist@0.0.8, minimist@~0.0.1: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + dependencies: + minimist "0.0.8" + +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mktemp@~0.3.4: + version "0.3.5" + resolved "https://registry.yarnpkg.com/mktemp/-/mktemp-0.3.5.tgz#a1504c706d0d2b198c6a0eb645f7fdaf8181f7de" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + +multipipe@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" + dependencies: + duplexer2 "0.0.2" + +mutationobserver-shim@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#f4d5dae7a4971a2207914fb5a90ebd514b65acca" + +mute-stream@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.4.tgz#a9219960a6d5d5d046597aee51252c6655f7177e" + +mute-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + +nan@^2.0.0, nan@^2.3.0, nan@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.0.tgz#aa8f1e34531d807e9e27755b234b4a6ec0c152a8" + +natives@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.0.tgz#e9ff841418a6b2ec7a495e939984f78f163e6e31" + +ncname@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" + dependencies: + xml-char-classes "^1.0.0" + +negotiator@0.4.9: + version "0.4.9" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.4.9.tgz#92e46b6db53c7e421ed64a2bc94f08be7630df3f" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +no-case@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081" + dependencies: + lower-case "^1.1.1" + +node-gyp@^3.3.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.5.0.tgz#a8fe5e611d079ec16348a3eb960e78e11c85274a" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + minimatch "^3.0.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "2" + rimraf "2" + semver "2.x || 3.x || 4 || 5" + tar "^2.0.0" + which "1" + +node-libs-browser@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-1.1.1.tgz#2a38243abedd7dffcd07a97c9aca5668975a6fea" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.1.4" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "0.0.1" + os-browserify "^0.2.0" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.0.5" + stream-browserify "^2.0.1" + stream-http "^2.3.1" + string_decoder "^0.10.25" + timers-browserify "^1.4.2" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-modules-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/node-modules-path/-/node-modules-path-1.0.1.tgz#40096b08ce7ad0ea14680863af449c7c75a5d1c8" + +node-pre-gyp@^0.6.29, node-pre-gyp@^0.6.4: + version "0.6.32" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz#fc452b376e7319b3d255f5f34853ef6fd8fe1fd5" + dependencies: + mkdirp "~0.5.1" + nopt "~3.0.6" + npmlog "^4.0.1" + rc "~1.1.6" + request "^2.79.0" + rimraf "~2.5.4" + semver "~5.3.0" + tar "~2.2.1" + tar-pack "~3.3.0" + +node-sass@^3.10.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-3.13.1.tgz#7240fbbff2396304b4223527ed3020589c004fc2" + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.clonedeep "^4.3.2" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.3.2" + node-gyp "^3.3.1" + npmlog "^4.0.0" + request "^2.61.0" + sass-graph "^2.1.1" + +node-status-codes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" + +node-uuid@~1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f" + +node-zopfli@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-zopfli/-/node-zopfli-2.0.2.tgz#a7a473ae92aaea85d4c68d45bbf2c944c46116b8" + dependencies: + commander "^2.8.1" + defaults "^1.0.2" + nan "^2.0.0" + node-pre-gyp "^0.6.4" + +"nopt@2 || 3", nopt@3.x, nopt@^3.0.1, nopt@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + +normalize-url@^1.4.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.0.tgz#c2bb50035edee62cd81edb2d45da68dc25e3423e" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +npm-run-all@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-3.1.2.tgz#c7e3faf4aa0a59bf0dcfc12601166151692171cf" + dependencies: + chalk "^1.1.3" + cross-spawn "^4.0.0" + minimatch "^3.0.2" + object-assign "^4.0.1" + pinkie-promise "^2.0.1" + ps-tree "^1.0.1" + read-pkg "^1.1.0" + read-pkg-up "^1.0.1" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.1" + set-blocking "~2.0.0" + +nth-check@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.0, oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@4.x.x, object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-assign@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + +object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.2.0.tgz#b5392bee9782da6d9fb7d6afaf539779f1234c2b" + dependencies: + isobject "^2.1.0" + +obuf@^1.0.0, obuf@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" + +offline-plugin@^3.4.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/offline-plugin/-/offline-plugin-3.4.2.tgz#ddbb9eec3e8fd052ec51e03c56afdc33b0a331a2" + dependencies: + deep-extend "^0.4.0" + ejs "^2.3.4" + es6-promise "^3.0.2" + loader-utils "0.2.x" + minimatch "^3.0.2" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + +once@1.x, once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + +opn@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.1.tgz#9bd30ee3eba4fd533be2c83d56329a4e58913bf8" + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +opn@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +optimist@^0.6.1, optimist@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +options@>=0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + +ora@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" + dependencies: + chalk "^1.1.1" + cli-cursor "^1.0.2" + cli-spinners "^0.1.2" + object-assign "^4.0.1" + +original@>=0.0.5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" + dependencies: + url-parse "1.0.x" + +os-browserify@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@0, osenv@^0.1.0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +package-json@^2.0.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb" + dependencies: + got "^5.0.0" + registry-auth-token "^3.0.1" + registry-url "^3.0.3" + semver "^5.1.0" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +param-case@2.1.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.0.tgz#2619f90fd6c829ed0b958f1c84ed03a745a6d70a" + dependencies: + no-case "^2.2.0" + +parse-asn1@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.1.0, parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse5@^2.1.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-2.2.3.tgz#0c4fc41c1000c5e6b93d48b03f8083837834e9f6" + +parsejson@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.1.tgz#9b10c6c0d825ab589e685153826de0a3ba278bcc" + dependencies: + better-assert "~1.0.0" + +parseqs@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.2.tgz#9dfe70b2cddac388bde4f35b1f240fa58adbe6c7" + dependencies: + better-assert "~1.0.0" + +parseuri@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.4.tgz#806582a39887e1ea18dd5e2fe0e01902268e9350" + dependencies: + better-assert "~1.0.0" + +parseurl@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-posix@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-posix/-/path-posix-1.0.0.tgz#06b26113f56beab042545a23bfa88003ccac260f" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + dependencies: + through "~2.3" + +pbkdf2@^3.0.3: + version "3.0.9" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + dependencies: + create-hmac "^1.1.2" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + +phantomjs-prebuilt@^2.1.7: + version "2.1.14" + resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0" + dependencies: + es6-promise "~4.0.3" + extract-zip "~1.5.0" + fs-extra "~1.0.0" + hasha "~2.2.0" + kew "~0.7.0" + progress "~1.1.8" + request "~2.79.0" + request-progress "~2.0.1" + which "~1.2.10" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0, pinkie-promise@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0, pinkie@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +popsicle-proxy-agent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/popsicle-proxy-agent/-/popsicle-proxy-agent-3.0.0.tgz#b9133c55d945759ab7ee61b7711364620d3aeadc" + dependencies: + http-proxy-agent "^1.0.0" + https-proxy-agent "^1.0.0" + +popsicle-retry@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/popsicle-retry/-/popsicle-retry-3.2.1.tgz#e06e866533b42a7a123eb330cbe63a7cebcba10c" + dependencies: + any-promise "^1.1.0" + xtend "^4.0.1" + +popsicle-rewrite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/popsicle-rewrite/-/popsicle-rewrite-1.0.0.tgz#1dd4e8ea9c3182351fb820f87934d992f7fb9007" + +popsicle-status@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/popsicle-status/-/popsicle-status-2.0.0.tgz#54e12722376efba0a353abdf53cbf1ce0e852efa" + +popsicle@^8.0.2: + version "8.2.0" + resolved "https://registry.yarnpkg.com/popsicle/-/popsicle-8.2.0.tgz#ff4401005cab43a9418a91410611c00197712d21" + dependencies: + any-promise "^1.3.0" + arrify "^1.0.0" + concat-stream "^1.4.7" + form-data "^2.0.0" + make-error-cause "^1.2.1" + throwback "^1.1.0" + tough-cookie "^2.0.0" + xtend "^4.0.0" + +portfinder@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.9.tgz#b1ac8755d092afc0433f1c4832fa17d6d1f5d830" + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + +postcss-calc@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + dependencies: + postcss "^5.0.2" + postcss-message-helpers "^2.0.0" + reduce-css-calc "^1.2.6" + +postcss-colormin@^2.1.8: + version "2.2.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.1.tgz#dc5421b6ae6f779ef6bfd47352b94abe59d0316b" + dependencies: + colormin "^1.0.5" + postcss "^5.0.13" + postcss-value-parser "^3.2.3" + +postcss-convert-values@^2.3.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.0.tgz#08c6d06130fe58a91a21ff50829e1aad6a3a1acc" + dependencies: + postcss "^5.0.11" + postcss-value-parser "^3.1.2" + +postcss-discard-comments@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" + dependencies: + postcss "^5.0.14" + +postcss-discard-duplicates@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.0.2.tgz#02be520e91571ffb10738766a981d5770989bb32" + dependencies: + postcss "^5.0.4" + +postcss-discard-empty@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" + dependencies: + postcss "^5.0.14" + +postcss-discard-overridden@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" + dependencies: + postcss "^5.0.16" + +postcss-discard-unused@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" + dependencies: + postcss "^5.0.14" + uniqs "^2.0.0" + +postcss-filter-plugins@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c" + dependencies: + postcss "^5.0.4" + uniqid "^4.0.0" + +postcss-loader@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-0.9.1.tgz#87a3e70f58e46d68a75badc6725d9ea4773fd1d7" + dependencies: + loader-utils "^0.2.14" + postcss "^5.0.19" + +postcss-merge-idents@^2.1.5: + version "2.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" + dependencies: + has "^1.0.1" + postcss "^5.0.10" + postcss-value-parser "^3.1.1" + +postcss-merge-longhand@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" + dependencies: + postcss "^5.0.4" + +postcss-merge-rules@^2.0.3: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.1.tgz#5e5640020ce43cddd343c73bba91c9a358d1fe0f" + dependencies: + browserslist "^1.5.2" + caniuse-api "^1.5.2" + postcss "^5.0.4" + postcss-selector-parser "^2.2.2" + vendors "^1.0.0" + +postcss-message-helpers@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" + +postcss-minify-font-values@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" + dependencies: + object-assign "^4.0.1" + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-minify-gradients@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" + dependencies: + postcss "^5.0.12" + postcss-value-parser "^3.3.0" + +postcss-minify-params@^1.0.4: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.2" + postcss-value-parser "^3.0.2" + uniqs "^2.0.0" + +postcss-minify-selectors@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" + dependencies: + alphanum-sort "^1.0.2" + has "^1.0.1" + postcss "^5.0.14" + postcss-selector-parser "^2.0.0" + +postcss-modules-extract-imports@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341" + dependencies: + postcss "^5.0.4" + +postcss-modules-local-by-default@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce" + dependencies: + css-selector-tokenizer "^0.6.0" + postcss "^5.0.4" + +postcss-modules-scope@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29" + dependencies: + css-selector-tokenizer "^0.6.0" + postcss "^5.0.4" + +postcss-modules-values@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1" + dependencies: + icss-replace-symbols "^1.0.2" + postcss "^5.0.14" + +postcss-normalize-charset@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" + dependencies: + postcss "^5.0.5" + +postcss-normalize-url@^3.0.7: + version "3.0.8" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^1.4.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + +postcss-ordered-values@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.1" + +postcss-reduce-idents@^2.2.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-reduce-initial@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" + dependencies: + postcss "^5.0.4" + +postcss-reduce-transforms@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" + dependencies: + has "^1.0.1" + postcss "^5.0.8" + postcss-value-parser "^3.0.1" + +postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.2.tgz#3d70f5adda130da51c7c0c2fc023f56b1374fe08" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^2.1.1: + version "2.1.6" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" + dependencies: + is-svg "^2.0.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + svgo "^0.7.0" + +postcss-unique-selectors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + +postcss-zindex@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" + dependencies: + has "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.19, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.8: + version "5.2.10" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.10.tgz#b58b64e04f66f838b7bc7cb41f7dac168568a945" + dependencies: + chalk "^1.1.3" + js-base64 "^2.1.9" + source-map "^0.5.6" + supports-color "^3.1.2" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.0, prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +pretty-error@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.0.2.tgz#a7db19cbb529ca9f0af3d3a2f77d5caf8e5dec23" + dependencies: + renderkid "~2.0.0" + utila "~0.4" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process-relative-require@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-relative-require/-/process-relative-require-1.0.0.tgz#1590dfcf5b8f2983ba53e398446b68240b4cc68a" + dependencies: + node-modules-path "^1.0.0" + +process@^0.11.0, process@~0.11.0: + version "0.11.9" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" + +progress@~1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + +promise-finally@^2.0.1, promise-finally@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/promise-finally/-/promise-finally-2.2.1.tgz#22616c4ba902916e988bd46c54d7caa08910cd77" + dependencies: + any-promise "^1.3.0" + +promise-map-series@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/promise-map-series/-/promise-map-series-0.2.3.tgz#c2d377afc93253f6bd03dbb77755eb88ab20a847" + dependencies: + rsvp "^3.0.14" + +promise@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" + dependencies: + asap "~2.0.3" + +protractor@4.0.9: + version "4.0.9" + resolved "https://registry.yarnpkg.com/protractor/-/protractor-4.0.9.tgz#152f72e3729b2576226e370dc308699cccfa77ba" + dependencies: + "@types/jasmine" "^2.2.31" + "@types/node" "^6.0.35" + "@types/q" "^0.0.30" + "@types/selenium-webdriver" "~2.53.30" + adm-zip "0.4.7" + chalk "^1.1.3" + glob "^7.0.3" + jasmine "2.5.2" + jasminewd2 "0.0.10" + optimist "~0.6.0" + q "1.4.1" + saucelabs "~1.3.0" + selenium-webdriver "2.53.3" + source-map-support "~0.4.0" + webdriver-manager "^10.2.2" + +protractor@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/protractor/-/protractor-3.3.0.tgz#7f4468306ac29a315086daf64a2b4a15c0bc1317" + dependencies: + adm-zip "0.4.7" + chalk "^1.1.3" + glob "~6.0" + jasmine "2.4.1" + jasminewd2 "0.0.9" + optimist "~0.6.0" + q "1.4.1" + request "~2.67.0" + saucelabs "~1.0.1" + selenium-webdriver "2.52.0" + source-map-support "~0.4.0" + +proxy-addr@~1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.3.tgz#dc97502f5722e888467b3fa2297a7b1ff47df074" + dependencies: + forwarded "~0.1.0" + ipaddr.js "1.2.0" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +ps-tree@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" + dependencies: + event-stream "~3.3.0" + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +q@1.4.1, q@^1.1.2, q@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" + +qjobs@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" + +qs@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" + +qs@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625" + +qs@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-5.2.1.tgz#801fee030e0b9450d6385adc48a4cc55b44aedfc" + +qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + +query-string@^4.1.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.1.tgz#54baada6713eafc92be75c47a731f2ebd09cd11d" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +querystringify@0.0.x: + version "0.0.4" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" + +quick-temp@0.1.5, quick-temp@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/quick-temp/-/quick-temp-0.1.5.tgz#0d0d67f0fb6a589a0e142f90985f76cdbaf403f7" + dependencies: + mktemp "~0.3.4" + rimraf "~2.2.6" + underscore.string "~2.3.3" + +randomatic@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +randombytes@^2.0.0, randombytes@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" + +range-parser@^1.0.3, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96" + dependencies: + bytes "2.4.0" + iconv-lite "0.4.15" + unpipe "1.0.0" + +raw-loader@^0.5.1, raw-loader@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" + +rc@^1.0.1, rc@^1.1.5, rc@^1.1.6, rc@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~1.0.4" + +read-all-stream@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" + dependencies: + pinkie-promise "^2.0.0" + readable-stream "^2.0.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0, read-pkg@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@1.0, readable-stream@~1.0.2: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~2.0.0, readable-stream@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-stream@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +readline2@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-0.1.1.tgz#99443ba6e83b830ef3051bfd7dc241a82728d568" + dependencies: + mute-stream "0.0.4" + strip-ansi "^2.0.1" + +readline2@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +redeyed@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-0.5.0.tgz#7ab000e60ee3875ac115d29edb32c1403c6c25d1" + dependencies: + esprima-fb "~12001.1.0-dev-harmony-fb" + +reduce-css-calc@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + dependencies: + balanced-match "^0.4.2" + +reflect-metadata@^0.1.2, reflect-metadata@^0.1.8: + version "0.1.9" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.9.tgz#987238dc87a516895fe457f130435ffbd763a4d4" + +regenerate@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" + +regenerator-runtime@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +registry-auth-token@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.1.0.tgz#997c08256e0c7999837b90e944db39d8a790276b" + dependencies: + rc "^1.1.6" + +registry-url@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + dependencies: + rc "^1.0.1" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + +remap-istanbul@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/remap-istanbul/-/remap-istanbul-0.6.4.tgz#ac551eff1aa641504b4f318d0303dda61e3bb695" + dependencies: + amdefine "1.0.0" + gulp-util "3.0.7" + istanbul "0.4.3" + source-map ">=0.5.6" + through2 "2.0.1" + +renderkid@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.0.tgz#1859753e7a5adbf35443aba0d4e4579e78abee85" + dependencies: + css-select "^1.1.0" + dom-converter "~0.1" + htmlparser2 "~3.3.0" + strip-ansi "^3.0.0" + utila "~0.3" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +replace-ext@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + +request-progress@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08" + dependencies: + throttleit "^1.0.0" + +request@2, request@>=2.42.0, request@^2.27.0, request@^2.61.0, request@^2.69.0, request@^2.72.0, request@^2.79.0, request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +request@~2.67.0: + version "2.67.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.67.0.tgz#8af74780e2bf11ea0ae9aa965c11f11afd272742" + dependencies: + aws-sign2 "~0.6.0" + bl "~1.0.0" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~1.0.0-rc3" + har-validator "~2.0.2" + hawk "~3.1.0" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.0" + qs "~5.2.0" + stringstream "~0.0.4" + tough-cookie "~2.2.0" + tunnel-agent "~0.4.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +requires-port@1.0.x, requires-port@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@^1.1.6, resolve@^1.1.7: + version "1.2.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.2, rimraf@^2.5.3, rimraf@~2.5.1, rimraf@~2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" + dependencies: + glob "^7.0.5" + +rimraf@~2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.1.4.tgz#5a6eb62eeda068f51ede50f29b3e5cd22f3d9bb2" + optionalDependencies: + graceful-fs "~1" + +rimraf@~2.2.6: + version "2.2.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + +ripemd160@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" + +rsvp@^3.0.14, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21: + version "3.3.3" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.3.3.tgz#34633caaf8bc66ceff4be3c2e1dffd032538a813" + +rsvp@~3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.2.1.tgz#07cb4a5df25add9e826ebc67dcc9fd89db27d84a" + +run-async@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + dependencies: + once "^1.3.0" + +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + +rxjs@5.0.0-beta.12: + version "5.0.0-beta.12" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.0.0-beta.12.tgz#cdfde2d8c4639d20ae7794bff8fddf32da7ad337" + dependencies: + symbol-observable "^1.0.1" + +rxjs@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.0.3.tgz#fc8bdf464ebf938812748e4196788f392fef9754" + dependencies: + symbol-observable "^1.0.1" + +sass-graph@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.1.2.tgz#965104be23e8103cb7e5f710df65935b317da57b" + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + yargs "^4.7.1" + +sass-loader@^3.2.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-3.2.3.tgz#742e81fd8170a8771a979e18622501674a88e355" + dependencies: + async "^1.4.0" + loader-utils "^0.2.5" + object-assign "^4.0.1" + +saucelabs@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.0.1.tgz#b50a100d9c5a4140748b35335a6e5d70017dadf9" + dependencies: + https-proxy-agent "^1.0.0" + +saucelabs@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.3.0.tgz#d240e8009df7fa87306ec4578a69ba3b5c424fee" + dependencies: + https-proxy-agent "^1.0.0" + +sax@0.5.x: + version "0.5.8" + resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" + +sax@0.6.x: + version "0.6.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-0.6.1.tgz#563b19c7c1de892e09bfc4f2fc30e3c27f0952b9" + +sax@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + +script-loader@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/script-loader/-/script-loader-0.7.0.tgz#685dc7e7069e0dee7a92674f0ebc5b0f55baa5ec" + dependencies: + raw-loader "~0.5.1" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + +selenium-webdriver@2.52.0: + version "2.52.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-2.52.0.tgz#d2dcb2f51b48733d6c42829e52767ecee2bf4b6b" + dependencies: + adm-zip "0.4.4" + rimraf "^2.2.8" + tmp "0.0.24" + ws "^1.0.1" + xml2js "0.4.4" + +selenium-webdriver@2.53.3: + version "2.53.3" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz#d29ff5a957dff1a1b49dc457756e4e4bfbdce085" + dependencies: + adm-zip "0.4.4" + rimraf "^2.2.8" + tmp "0.0.24" + ws "^1.0.1" + xml2js "0.4.4" + +semver-diff@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + dependencies: + semver "^5.0.3" + +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +semver@~4.3.3: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + +semver@~5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" + +send@0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" + dependencies: + debug "~2.2.0" + depd "~1.1.0" + destroy "~1.0.4" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + fresh "0.3.0" + http-errors "~1.5.0" + mime "1.3.4" + ms "0.7.1" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.3.0" + +serve-index@^1.7.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b" + dependencies: + accepts "~1.3.3" + batch "0.5.3" + debug "~2.2.0" + escape-html "~1.0.3" + http-errors "~1.5.0" + mime-types "~2.1.11" + parseurl "~1.3.1" + +serve-static@~1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.1" + send "0.14.1" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +setprototypeof@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" + +sha.js@^2.3.6: + version "2.4.8" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" + dependencies: + inherits "^2.0.1" + +shell-quote@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + +shelljs@^0.7.0: + version "0.7.6" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +silent-error@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/silent-error/-/silent-error-1.0.1.tgz#71b7d503d1c6f94882b51b56be879b113cb4822c" + dependencies: + debug "^2.2.0" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + +slide@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +socket.io-adapter@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.4.0.tgz#fb9f82ab1aa65290bf72c3657955b930a991a24f" + dependencies: + debug "2.2.0" + socket.io-parser "2.2.2" + +socket.io-client@1.4.6: + version "1.4.6" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.4.6.tgz#49b0ba537efd15b8297c84016e642e1c7c752c3d" + dependencies: + backo2 "1.0.2" + component-bind "1.0.0" + component-emitter "1.2.0" + debug "2.2.0" + engine.io-client "1.6.9" + has-binary "0.1.7" + indexof "0.0.1" + object-component "0.0.3" + parseuri "0.0.4" + socket.io-parser "2.2.6" + to-array "0.1.4" + +socket.io-parser@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.2.2.tgz#3d7af6b64497e956b7d9fe775f999716027f9417" + dependencies: + benchmark "1.0.0" + component-emitter "1.1.2" + debug "0.7.4" + isarray "0.0.1" + json3 "3.2.6" + +socket.io-parser@2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.2.6.tgz#38dfd61df50dcf8ab1d9e2091322bf902ba28b99" + dependencies: + benchmark "1.0.0" + component-emitter "1.1.2" + debug "2.2.0" + isarray "0.0.1" + json3 "3.3.2" + +socket.io@1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.4.7.tgz#92b7f7cb88c5797d4daee279fe8075dbe6d3fa1c" + dependencies: + debug "2.2.0" + engine.io "1.6.10" + has-binary "0.1.7" + socket.io-adapter "0.4.0" + socket.io-client "1.4.6" + socket.io-parser "2.2.6" + +sockjs-client@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.1.tgz#284843e9a9784d7c474b1571b3240fca9dda4bb0" + dependencies: + debug "^2.2.0" + eventsource "~0.1.6" + faye-websocket "~0.11.0" + inherits "^2.0.1" + json3 "^3.3.2" + url-parse "^1.1.1" + +sockjs@0.3.18: + version "0.3.18" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" + dependencies: + faye-websocket "^0.10.0" + uuid "^2.0.2" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^0.1.4, source-list-map@~0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" + +source-map-loader@^0.1.5: + version "0.1.6" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.1.6.tgz#c09903da6d73b9e53b7ed8ee5245597051e98e91" + dependencies: + async "^0.9.0" + loader-utils "~0.2.2" + source-map "~0.1.33" + +source-map-support@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.3.3.tgz#34900977d5ba3f07c7757ee72e73bb1a9b53754f" + dependencies: + source-map "0.1.32" + +source-map-support@^0.4.0, source-map-support@^0.4.2, source-map-support@~0.4.0: + version "0.4.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.10.tgz#d7b19038040a14c0837a18e630a196453952b378" + dependencies: + source-map "^0.5.3" + +source-map@0.1.32: + version "0.1.32" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" + dependencies: + amdefine ">=0.0.4" + +source-map@0.1.x, source-map@^0.1.41, source-map@~0.1.33: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + dependencies: + amdefine ">=0.0.4" + +source-map@0.4.x, source-map@^0.4.2, source-map@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@>=0.5.6, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + dependencies: + amdefine ">=0.0.4" + +sourcemap-istanbul-instrumenter-loader@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/sourcemap-istanbul-instrumenter-loader/-/sourcemap-istanbul-instrumenter-loader-0.2.0.tgz#8fd72e8abd16ded58918a7474db5bb3d0dbbcacd" + dependencies: + istanbul "0.x.x" + loader-utils "0.x.x" + object-assign "4.x.x" + +sparkles@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +spdy-transport@^2.0.15: + version "2.0.18" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.18.tgz#43fc9c56be2cccc12bb3e2754aa971154e836ea6" + dependencies: + debug "^2.2.0" + hpack.js "^2.1.6" + obuf "^1.1.0" + readable-stream "^2.0.1" + wbuf "^1.4.0" + +spdy@^3.4.1: + version "3.4.4" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.4.tgz#e0406407ca90ff01b553eb013505442649f5a819" + dependencies: + debug "^2.2.0" + handle-thing "^1.2.4" + http-deceiver "^1.2.4" + select-hose "^2.0.0" + spdy-transport "^2.0.15" + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + dependencies: + through "2" + +sprintf-js@^1.0.3, sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.2.tgz#d5a804ce22695515638e798dbe23273de070a5fa" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jodid25519 "^1.0.0" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +"statuses@>= 1.3.1 < 2", statuses@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + dependencies: + duplexer "~0.1.1" + +stream-http@^2.3.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.2.tgz#bdfe40d2ee9262eb6bf2255bb3ad0ec0cdd6526d" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.1.0" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-replace-loader@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-1.0.5.tgz#e2b0d4fcd611f0d41ca433bbf50e029450e37a3d" + dependencies: + loader-utils "^0.2.11" + lodash "^3.10.1" + +string-template@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string.prototype.padend@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.4.3" + function-bind "^1.0.2" + +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-2.0.1.tgz#df62c1aa94ed2f114e1d0f21fd1d50482b79a60e" + dependencies: + ansi-regex "^1.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@^1.0.2, strip-json-comments@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" + +strip-json-comments@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +style-loader@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.1.tgz#468280efbc0473023cd3a6cd56e33b5a1d7fc3a9" + dependencies: + loader-utils "^0.2.7" + +stylus-loader@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-2.4.0.tgz#2ffbf2854a2071086b88bc338f423ad985b43544" + dependencies: + loader-utils "^0.2.9" + when "~3.6.x" + +stylus@^0.54.5: + version "0.54.5" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79" + dependencies: + css-parse "1.7.x" + debug "*" + glob "7.0.x" + mkdirp "0.5.x" + sax "0.5.x" + source-map "0.1.x" + +sum-up@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sum-up/-/sum-up-1.0.3.tgz#1c661f667057f63bcb7875aa1438bc162525156e" + dependencies: + chalk "^1.0.0" + +supports-color@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +svgo@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.1.tgz#287320fed972cb097e72c2bb1685f96fe08f8034" + dependencies: + coa "~1.0.1" + colors "~1.1.2" + csso "~2.2.1" + js-yaml "~3.6.1" + mkdirp "~0.5.1" + sax "~1.2.1" + whet.extend "~0.9.9" + +symbol-observable@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" + +symlink-or-copy@^1.0.0, symlink-or-copy@^1.0.1, symlink-or-copy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/symlink-or-copy/-/symlink-or-copy-1.1.8.tgz#cabe61e0010c1c023c173b25ee5108b37f4b4aa3" + +tapable@^0.1.8: + version "0.1.10" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" + +tapable@^0.2.3, tapable@^0.2.5, tapable@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" + +tar-pack@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" + dependencies: + debug "~2.2.0" + fstream "~1.0.10" + fstream-ignore "~1.0.5" + once "~1.3.3" + readable-stream "~2.1.4" + rimraf "~2.5.1" + tar "~2.2.1" + uid-number "~0.0.6" + +tar@^2.0.0, tar@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +temp@0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" + dependencies: + os-tmpdir "^1.0.0" + rimraf "~2.2.6" + +temp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.5.1.tgz#77ab19c79aa7b593cbe4fac2441768cad987b8df" + dependencies: + rimraf "~2.1.4" + +tether@^1.3.7: + version "1.4.0" + resolved "https://registry.yarnpkg.com/tether/-/tether-1.4.0.tgz#0f9fa171f75bf58485d8149e94799d7ae74d1c1a" + +"textextensions@1 || 2": + version "2.0.1" + resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.0.1.tgz#be8cf22d65379c151319f88f0335ad8f667abdca" + +thenify@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.2.1.tgz#251fd1c80aff6e5cf57cb179ab1fcb724269bd11" + dependencies: + any-promise "^1.0.0" + +throat@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-3.0.0.tgz#e7c64c867cbb3845f10877642f7b60055b8ec0d6" + +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + +through2@2.0.1, through2@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" + dependencies: + readable-stream "~2.0.0" + xtend "~4.0.0" + +through@2, through@X.X.X, through@^2.3.6, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +throwback@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/throwback/-/throwback-1.1.1.tgz#f007e7c17604a6d16d7a07c41aa0e8fedc6184a4" + dependencies: + any-promise "^1.3.0" + +time-stamp@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.0.1.tgz#9f4bd23559c9365966f3302dbba2b07c6b99b151" + +timed-out@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217" + +timers-browserify@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + dependencies: + process "~0.11.0" + +tmp@0.0.24: + version "0.0.24" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12" + +tmp@0.0.28: + version "0.0.28" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" + dependencies: + os-tmpdir "~1.0.1" + +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +toposort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.0.tgz#b66cf385a1a8a8e68e45b8259e7f55875e8b06ef" + +touch@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" + dependencies: + nopt "~1.0.10" + +tough-cookie@^2.0.0, tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +tough-cookie@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.2.2.tgz#c83a1830f4e5ef0b93ef2a3488e724f8de016ac7" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +ts-helpers@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ts-helpers/-/ts-helpers-1.1.2.tgz#fc69be9f1f3baed01fb1a0ef8d4cfe748814d835" + +ts-loader@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-0.8.2.tgz#7331296d13d5b3105cd905cebca39143eed2b255" + dependencies: + arrify "^1.0.0" + colors "^1.0.3" + enhanced-resolve "^0.9.0" + loader-utils "^0.2.6" + object-assign "^2.0.0" + semver "^5.0.1" + +ts-node@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-1.2.1.tgz#108f1e43366e25f5223e8549fa7c6dcf780eda23" + dependencies: + arrify "^1.0.0" + chalk "^1.1.1" + diff "^2.1.1" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + pinkie "^2.0.4" + source-map-support "^0.4.0" + tsconfig "^5.0.2" + xtend "^4.0.0" + +tsconfig@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-5.0.3.tgz#5f4278e701800967a8fc383fd19648878f2a6e3a" + dependencies: + any-promise "^1.3.0" + parse-json "^2.2.0" + strip-bom "^2.0.0" + strip-json-comments "^2.0.0" + +tsickle@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.1.7.tgz#bfe8f4cdcdaf9a40b84a729a38480c2f824f18ab" + dependencies: + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map "^0.4.2" + source-map-support "^0.3.1" + +tsickle@^0.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.2.4.tgz#98f4837bf45eb142a90fec751f8e30c6a5bc7c06" + dependencies: + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map "^0.5.6" + source-map-support "^0.4.2" + +tslint-loader@^2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/tslint-loader/-/tslint-loader-2.1.5.tgz#77abdfd9bf13d7133a6efa4447a1690783c4bb49" + dependencies: + loader-utils "^0.2.7" + mkdirp "^0.5.1" + object-assign "^4.0.1" + rimraf "^2.4.4" + strip-json-comments "^1.0.2" + +tslint@^3.15.1: + version "3.15.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-3.15.1.tgz#da165ca93d8fdc2c086b51165ee1bacb48c98ea5" + dependencies: + colors "^1.1.2" + diff "^2.2.1" + findup-sync "~0.3.0" + glob "^7.0.3" + optimist "~0.6.0" + resolve "^1.1.7" + underscore.string "^3.3.4" + +tslint@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-4.3.1.tgz#28f679c53ca4b273688bcb6fcf0dde7ff1bb2169" + dependencies: + babel-code-frame "^6.20.0" + colors "^1.1.2" + diff "^3.0.1" + findup-sync "~0.3.0" + glob "^7.1.1" + optimist "~0.6.0" + resolve "^1.1.7" + underscore.string "^3.3.4" + update-notifier "^1.0.2" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-is@~1.6.13, type-is@~1.6.14: + version "1.6.14" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.13" + +typedarray@~0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +typescript@^2.0.3, typescript@~2.0.3: + version "2.0.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.0.10.tgz#ccdd4ed86fd5550a407101a0814012e1b3fac3dd" + +typings-core@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/typings-core/-/typings-core-1.6.1.tgz#ce4b2931ea2f19bb8f3dacbec69983ac4e964a37" + dependencies: + any-promise "^1.3.0" + array-uniq "^1.0.2" + configstore "^2.0.0" + debug "^2.2.0" + detect-indent "^4.0.0" + graceful-fs "^4.1.2" + has "^1.0.1" + invariant "^2.2.0" + is-absolute "^0.2.3" + listify "^1.0.0" + lockfile "^1.0.1" + make-error-cause "^1.2.1" + mkdirp "^0.5.1" + object.pick "^1.1.1" + parse-json "^2.2.0" + popsicle "^8.0.2" + popsicle-proxy-agent "^3.0.0" + popsicle-retry "^3.2.0" + popsicle-rewrite "^1.0.0" + popsicle-status "^2.0.0" + promise-finally "^2.0.1" + rc "^1.1.5" + rimraf "^2.4.4" + sort-keys "^1.0.0" + string-template "^1.0.0" + strip-bom "^2.0.0" + thenify "^3.1.0" + throat "^3.0.0" + touch "^1.0.0" + typescript "^2.0.3" + xtend "^4.0.0" + zip-object "^0.1.0" + +typings@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/typings/-/typings-1.5.0.tgz#b9d236cf1d37460854f8c671ea495d9405b8103f" + dependencies: + any-promise "^1.3.0" + archy "^1.0.0" + bluebird "^3.1.1" + chalk "^1.0.0" + cli-truncate "^0.2.1" + columnify "^1.5.2" + elegant-spinner "^1.0.1" + has-unicode "^2.0.1" + listify "^1.0.0" + log-update "^1.0.2" + minimist "^1.2.0" + promise-finally "^2.2.1" + typings-core "^1.6.1" + update-notifier "^1.0.0" + wordwrap "^1.0.0" + xtend "^4.0.1" + +uc.micro@^1.0.0, uc.micro@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" + +uglify-js@2.7.x, uglify-js@^2.6, uglify-js@~2.7.3: + version "2.7.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" + dependencies: + async "~0.2.6" + source-map "~0.5.1" + uglify-to-browserify "~1.0.0" + yargs "~3.10.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uid-number@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +ultron@1.0.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + +unc-path-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + +underscore.string@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" + dependencies: + sprintf-js "^1.0.3" + util-deprecate "^1.0.2" + +underscore.string@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.3.3.tgz#71c08bf6b428b1133f37e78fa3a21c82f7329b0d" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +uniqid@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1" + dependencies: + macaddress "^0.2.8" + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +unzip-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" + +update-notifier@^1.0.0, update-notifier@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-1.0.3.tgz#8f92c515482bd6831b7c93013e70f87552c7cf5a" + dependencies: + boxen "^0.6.0" + chalk "^1.0.0" + configstore "^2.0.0" + is-npm "^1.0.0" + latest-version "^2.0.0" + lazy-req "^1.1.0" + semver-diff "^2.0.0" + xdg-basedir "^2.0.0" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + +url-loader@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.7.tgz#67e8779759f8000da74994906680c943a9b0925d" + dependencies: + loader-utils "0.2.x" + mime "1.2.x" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + dependencies: + prepend-http "^1.0.1" + +url-parse@1.0.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + +url-parse@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +urlgrey@>=0.4.0: + version "0.4.4" + resolved "https://registry.yarnpkg.com/urlgrey/-/urlgrey-0.4.4.tgz#892fe95960805e85519f1cd4389f2cb4cbb7652f" + +useragent@^2.1.9: + version "2.1.10" + resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.10.tgz#2456c5c2b722b47f3d8321ed257b490a3d9bb2af" + dependencies: + lru-cache "2.2.x" + +utf8@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.0.tgz#0cfec5c8052d44a23e3aaa908104e8075f95dfd5" + +util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3, util@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +utila@~0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.3.3.tgz#d7e8e7d7e309107092b05f8d9688824d633a4226" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + +utils-merge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" + +uuid@^2.0.1, uuid@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + +uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +vary@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" + +vendors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +vinyl@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +vlq@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.1.tgz#14439d711891e682535467f8587c5630e4222a6c" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + +walk-sync@^0.2.6: + version "0.2.7" + resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.2.7.tgz#b49be4ee6867657aeb736978b56a29d10fa39969" + dependencies: + ensure-posix-path "^1.0.0" + matcher-collection "^1.0.0" + +walk-sync@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.1.tgz#558a16aeac8c0db59c028b73c66f397684ece465" + dependencies: + ensure-posix-path "^1.0.0" + matcher-collection "^1.0.0" + +watchpack@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.2.0.tgz#15d4620f1e7471f13fcb551d5c030d2c3eb42dbb" + dependencies: + async "^2.1.2" + chokidar "^1.4.3" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.4.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe" + dependencies: + minimalistic-assert "^1.0.0" + +wcwidth@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + dependencies: + defaults "^1.0.3" + +web-animations-js@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/web-animations-js/-/web-animations-js-2.2.2.tgz#7c3aa5382c5eade70cd206880d56a37869630638" + +webdriver-manager@10.2.5, webdriver-manager@^10.2.2: + version "10.2.5" + resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-10.2.5.tgz#6433c1a64b038388c295ed0dc9daa71e5df5024e" + dependencies: + adm-zip "^0.4.7" + chalk "^1.1.1" + del "^2.2.0" + glob "^7.0.3" + ini "^1.3.4" + minimist "^1.2.0" + q "^1.4.1" + request "^2.69.0" + rimraf "^2.5.2" + semver "^5.3.0" + +webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.4.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.9.0.tgz#a1c67a3dfd8a5c5d62740aa0babe61758b4c84aa" + dependencies: + memory-fs "~0.4.1" + mime "^1.3.4" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + +webpack-dev-server@2.1.0-beta.9: + version "2.1.0-beta.9" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.1.0-beta.9.tgz#45745a4a6b84849c63e3a21dfab3be7bdb897554" + dependencies: + chokidar "^1.6.0" + compression "^1.5.2" + connect-history-api-fallback "^1.3.0" + express "^4.13.3" + http-proxy-middleware "~0.17.1" + opn "4.0.2" + serve-index "^1.7.2" + sockjs "0.3.18" + sockjs-client "1.1.1" + spdy "^3.4.1" + strip-ansi "^3.0.0" + supports-color "^3.1.1" + webpack-dev-middleware "^1.4.0" + yargs "^4.7.1" + +webpack-md5-hash@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/webpack-md5-hash/-/webpack-md5-hash-0.0.5.tgz#d9f1899ead664459dd8b6b0c926ac71cfbd7bc7a" + dependencies: + md5 "^2.0.0" + +webpack-merge@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-0.14.1.tgz#d6bfe6d9360a024e1e7f8e6383ae735f1737cd23" + dependencies: + lodash.find "^3.2.1" + lodash.isequal "^4.2.0" + lodash.isplainobject "^3.2.0" + lodash.merge "^3.3.2" + +webpack-sources@^0.1.0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.4.tgz#ccc2c817e08e5fa393239412690bb481821393cd" + dependencies: + source-list-map "~0.1.7" + source-map "~0.5.3" + +webpack@2.1.0-beta.25: + version "2.1.0-beta.25" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.1.0-beta.25.tgz#c35ff4da4ee70344a2f14c35258d95412709e9ed" + dependencies: + acorn "^3.2.0" + ajv "^4.7.0" + async "^1.3.0" + clone "^1.0.2" + enhanced-resolve "^2.2.0" + interpret "^1.0.0" + loader-runner "^2.2.0" + loader-utils "^0.2.11" + memory-fs "~0.3.0" + mkdirp "~0.5.0" + node-libs-browser "^1.0.0" + object-assign "^4.0.1" + source-map "^0.5.3" + supports-color "^3.1.0" + tapable "~0.2.3" + uglify-js "~2.7.3" + watchpack "^1.0.0" + webpack-sources "^0.1.0" + yargs "^4.7.1" + +websocket-driver@>=0.5.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + dependencies: + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" + +when@~3.6.x: + version "3.6.4" + resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" + +whet.extend@~0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which@1, which@^1.1.1, which@^1.2.9, which@~1.2.10: + version "1.2.12" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" + dependencies: + isexe "^1.1.1" + +wide-align@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + dependencies: + string-width "^1.0.1" + +widest-line@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c" + dependencies: + string-width "^1.0.1" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@^1.0.0, wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-atomic@^1.1.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.1.tgz#7d45ba32316328dd1ec7d90f60ebc0d845bb759a" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + slide "^1.1.5" + +ws@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.0.1.tgz#7d0b2a2e58cddd819039c29c9de65045e1b310e9" + dependencies: + options ">=0.0.5" + ultron "1.0.x" + +ws@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018" + dependencies: + options ">=0.0.5" + ultron "1.0.x" + +xdg-basedir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" + dependencies: + os-homedir "^1.0.0" + +xml-char-classes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" + +xml2js@0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.4.tgz#3111010003008ae19240eba17497b57c729c555d" + dependencies: + sax "0.6.x" + xmlbuilder ">=1.0.0" + +xmlbuilder@>=1.0.0: + version "8.2.2" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773" + +xmlhttprequest-ssl@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.1.tgz#3b7741fea4a86675976e908d296d4445961faa67" + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yallist@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.0.0.tgz#306c543835f09ee1a4cb23b7bce9ab341c91cdd4" + +yam@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/yam/-/yam-0.0.18.tgz#e5cab771f0fc80ca599814cb9c269cb8bff00e2c" + dependencies: + findup "^0.1.5" + fs-extra "^0.16.3" + lodash.merge "^3.0.2" + +yargs-parser@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.0.6" + +yargs@^4.7.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.0.3" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.1" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^2.4.1" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + dependencies: + fd-slicer "~1.0.1" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + +zip-object@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/zip-object/-/zip-object-0.1.0.tgz#c1a0da04c88c837756e248680a03ff902ec3f53a" + +zone.js@^0.6.23: + version "0.6.26" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.6.26.tgz#067c13b8b80223a89b62e9dc82680f09762c4636" + +zone.js@^0.7.2: + version "0.7.6" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/src/vendor/github.com/agl/ed25519/LICENSE b/src/vendor/github.com/agl/ed25519/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/src/vendor/github.com/agl/ed25519/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/vendor/github.com/agl/ed25519/ed25519.go b/src/vendor/github.com/agl/ed25519/ed25519.go new file mode 100644 index 000000000..e5f873f52 --- /dev/null +++ b/src/vendor/github.com/agl/ed25519/ed25519.go @@ -0,0 +1,127 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ed25519 implements the Ed25519 signature algorithm. See +// http://ed25519.cr.yp.to/. +package ed25519 + +// This code is a port of the public domain, "ref10" implementation of ed25519 +// from SUPERCOP. + +import ( + "crypto/sha512" + "crypto/subtle" + "io" + + "github.com/agl/ed25519/edwards25519" +) + +const ( + PublicKeySize = 32 + PrivateKeySize = 64 + SignatureSize = 64 +) + +// GenerateKey generates a public/private key pair using randomness from rand. +func GenerateKey(rand io.Reader) (publicKey *[PublicKeySize]byte, privateKey *[PrivateKeySize]byte, err error) { + privateKey = new([64]byte) + publicKey = new([32]byte) + _, err = io.ReadFull(rand, privateKey[:32]) + if err != nil { + return nil, nil, err + } + + h := sha512.New() + h.Write(privateKey[:32]) + digest := h.Sum(nil) + + digest[0] &= 248 + digest[31] &= 127 + digest[31] |= 64 + + var A edwards25519.ExtendedGroupElement + var hBytes [32]byte + copy(hBytes[:], digest) + edwards25519.GeScalarMultBase(&A, &hBytes) + A.ToBytes(publicKey) + + copy(privateKey[32:], publicKey[:]) + return +} + +// Sign signs the message with privateKey and returns a signature. +func Sign(privateKey *[PrivateKeySize]byte, message []byte) *[SignatureSize]byte { + h := sha512.New() + h.Write(privateKey[:32]) + + var digest1, messageDigest, hramDigest [64]byte + var expandedSecretKey [32]byte + h.Sum(digest1[:0]) + copy(expandedSecretKey[:], digest1[:]) + expandedSecretKey[0] &= 248 + expandedSecretKey[31] &= 63 + expandedSecretKey[31] |= 64 + + h.Reset() + h.Write(digest1[32:]) + h.Write(message) + h.Sum(messageDigest[:0]) + + var messageDigestReduced [32]byte + edwards25519.ScReduce(&messageDigestReduced, &messageDigest) + var R edwards25519.ExtendedGroupElement + edwards25519.GeScalarMultBase(&R, &messageDigestReduced) + + var encodedR [32]byte + R.ToBytes(&encodedR) + + h.Reset() + h.Write(encodedR[:]) + h.Write(privateKey[32:]) + h.Write(message) + h.Sum(hramDigest[:0]) + var hramDigestReduced [32]byte + edwards25519.ScReduce(&hramDigestReduced, &hramDigest) + + var s [32]byte + edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced) + + signature := new([64]byte) + copy(signature[:], encodedR[:]) + copy(signature[32:], s[:]) + return signature +} + +// Verify returns true iff sig is a valid signature of message by publicKey. +func Verify(publicKey *[PublicKeySize]byte, message []byte, sig *[SignatureSize]byte) bool { + if sig[63]&224 != 0 { + return false + } + + var A edwards25519.ExtendedGroupElement + if !A.FromBytes(publicKey) { + return false + } + edwards25519.FeNeg(&A.X, &A.X) + edwards25519.FeNeg(&A.T, &A.T) + + h := sha512.New() + h.Write(sig[:32]) + h.Write(publicKey[:]) + h.Write(message) + var digest [64]byte + h.Sum(digest[:0]) + + var hReduced [32]byte + edwards25519.ScReduce(&hReduced, &digest) + + var R edwards25519.ProjectiveGroupElement + var b [32]byte + copy(b[:], sig[32:]) + edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b) + + var checkR [32]byte + R.ToBytes(&checkR) + return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1 +} diff --git a/src/vendor/github.com/agl/ed25519/edwards25519/const.go b/src/vendor/github.com/agl/ed25519/edwards25519/const.go new file mode 100644 index 000000000..ea5b77a71 --- /dev/null +++ b/src/vendor/github.com/agl/ed25519/edwards25519/const.go @@ -0,0 +1,1411 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package edwards25519 + +var d = FieldElement{ + -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116, +} + +var d2 = FieldElement{ + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199, +} + +var SqrtM1 = FieldElement{ + -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482, +} + +var A = FieldElement{ + 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} + +var bi = [8]PreComputedGroupElement{ + { + FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, + FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, + FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}, + }, + { + FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, + FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, + FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}, + }, + { + FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, + FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, + FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}, + }, + { + FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, + FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, + FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}, + }, + { + FieldElement{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877}, + FieldElement{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951}, + FieldElement{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784}, + }, + { + FieldElement{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436}, + FieldElement{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918}, + FieldElement{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877}, + }, + { + FieldElement{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800}, + FieldElement{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305}, + FieldElement{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300}, + }, + { + FieldElement{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876}, + FieldElement{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619}, + FieldElement{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683}, + }, +} + +var base = [32][8]PreComputedGroupElement{ + { + { + FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605}, + FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378}, + FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}, + }, + { + FieldElement{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303}, + FieldElement{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081}, + FieldElement{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697}, + }, + { + FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024}, + FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574}, + FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}, + }, + { + FieldElement{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540}, + FieldElement{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397}, + FieldElement{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325}, + }, + { + FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380}, + FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306}, + FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}, + }, + { + FieldElement{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777}, + FieldElement{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737}, + FieldElement{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652}, + }, + { + FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766}, + FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701}, + FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}, + }, + { + FieldElement{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726}, + FieldElement{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955}, + FieldElement{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425}, + }, + }, + { + { + FieldElement{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171}, + FieldElement{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510}, + FieldElement{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660}, + }, + { + FieldElement{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639}, + FieldElement{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963}, + FieldElement{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950}, + }, + { + FieldElement{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568}, + FieldElement{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335}, + FieldElement{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628}, + }, + { + FieldElement{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007}, + FieldElement{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772}, + FieldElement{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653}, + }, + { + FieldElement{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567}, + FieldElement{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686}, + FieldElement{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372}, + }, + { + FieldElement{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887}, + FieldElement{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954}, + FieldElement{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953}, + }, + { + FieldElement{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833}, + FieldElement{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532}, + FieldElement{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876}, + }, + { + FieldElement{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268}, + FieldElement{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214}, + FieldElement{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038}, + }, + }, + { + { + FieldElement{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800}, + FieldElement{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645}, + FieldElement{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664}, + }, + { + FieldElement{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933}, + FieldElement{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182}, + FieldElement{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222}, + }, + { + FieldElement{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991}, + FieldElement{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880}, + FieldElement{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092}, + }, + { + FieldElement{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295}, + FieldElement{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788}, + FieldElement{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553}, + }, + { + FieldElement{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026}, + FieldElement{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347}, + FieldElement{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033}, + }, + { + FieldElement{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395}, + FieldElement{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278}, + FieldElement{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890}, + }, + { + FieldElement{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995}, + FieldElement{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596}, + FieldElement{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891}, + }, + { + FieldElement{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060}, + FieldElement{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608}, + FieldElement{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606}, + }, + }, + { + { + FieldElement{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389}, + FieldElement{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016}, + FieldElement{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341}, + }, + { + FieldElement{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505}, + FieldElement{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553}, + FieldElement{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655}, + }, + { + FieldElement{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220}, + FieldElement{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631}, + FieldElement{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099}, + }, + { + FieldElement{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556}, + FieldElement{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749}, + FieldElement{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930}, + }, + { + FieldElement{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391}, + FieldElement{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253}, + FieldElement{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066}, + }, + { + FieldElement{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958}, + FieldElement{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082}, + FieldElement{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383}, + }, + { + FieldElement{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521}, + FieldElement{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807}, + FieldElement{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948}, + }, + { + FieldElement{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134}, + FieldElement{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455}, + FieldElement{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629}, + }, + }, + { + { + FieldElement{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069}, + FieldElement{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746}, + FieldElement{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919}, + }, + { + FieldElement{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837}, + FieldElement{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906}, + FieldElement{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771}, + }, + { + FieldElement{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817}, + FieldElement{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098}, + FieldElement{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409}, + }, + { + FieldElement{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504}, + FieldElement{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727}, + FieldElement{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420}, + }, + { + FieldElement{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003}, + FieldElement{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605}, + FieldElement{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384}, + }, + { + FieldElement{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701}, + FieldElement{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683}, + FieldElement{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708}, + }, + { + FieldElement{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563}, + FieldElement{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260}, + FieldElement{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387}, + }, + { + FieldElement{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672}, + FieldElement{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686}, + FieldElement{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665}, + }, + }, + { + { + FieldElement{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182}, + FieldElement{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277}, + FieldElement{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628}, + }, + { + FieldElement{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474}, + FieldElement{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539}, + FieldElement{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822}, + }, + { + FieldElement{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970}, + FieldElement{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756}, + FieldElement{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508}, + }, + { + FieldElement{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683}, + FieldElement{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655}, + FieldElement{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158}, + }, + { + FieldElement{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125}, + FieldElement{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839}, + FieldElement{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664}, + }, + { + FieldElement{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294}, + FieldElement{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899}, + FieldElement{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070}, + }, + { + FieldElement{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294}, + FieldElement{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949}, + FieldElement{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083}, + }, + { + FieldElement{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420}, + FieldElement{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940}, + FieldElement{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396}, + }, + }, + { + { + FieldElement{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567}, + FieldElement{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127}, + FieldElement{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294}, + }, + { + FieldElement{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887}, + FieldElement{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964}, + FieldElement{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195}, + }, + { + FieldElement{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244}, + FieldElement{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999}, + FieldElement{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762}, + }, + { + FieldElement{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274}, + FieldElement{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236}, + FieldElement{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605}, + }, + { + FieldElement{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761}, + FieldElement{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884}, + FieldElement{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482}, + }, + { + FieldElement{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638}, + FieldElement{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490}, + FieldElement{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170}, + }, + { + FieldElement{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736}, + FieldElement{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124}, + FieldElement{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392}, + }, + { + FieldElement{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029}, + FieldElement{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048}, + FieldElement{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958}, + }, + }, + { + { + FieldElement{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593}, + FieldElement{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071}, + FieldElement{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692}, + }, + { + FieldElement{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687}, + FieldElement{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441}, + FieldElement{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001}, + }, + { + FieldElement{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460}, + FieldElement{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007}, + FieldElement{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762}, + }, + { + FieldElement{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005}, + FieldElement{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674}, + FieldElement{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035}, + }, + { + FieldElement{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590}, + FieldElement{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957}, + FieldElement{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812}, + }, + { + FieldElement{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740}, + FieldElement{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122}, + FieldElement{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158}, + }, + { + FieldElement{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885}, + FieldElement{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140}, + FieldElement{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857}, + }, + { + FieldElement{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155}, + FieldElement{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260}, + FieldElement{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483}, + }, + }, + { + { + FieldElement{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677}, + FieldElement{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815}, + FieldElement{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751}, + }, + { + FieldElement{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203}, + FieldElement{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208}, + FieldElement{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230}, + }, + { + FieldElement{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850}, + FieldElement{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389}, + FieldElement{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968}, + }, + { + FieldElement{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689}, + FieldElement{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880}, + FieldElement{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304}, + }, + { + FieldElement{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632}, + FieldElement{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412}, + FieldElement{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566}, + }, + { + FieldElement{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038}, + FieldElement{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232}, + FieldElement{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943}, + }, + { + FieldElement{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856}, + FieldElement{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738}, + FieldElement{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971}, + }, + { + FieldElement{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718}, + FieldElement{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697}, + FieldElement{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883}, + }, + }, + { + { + FieldElement{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912}, + FieldElement{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358}, + FieldElement{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849}, + }, + { + FieldElement{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307}, + FieldElement{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977}, + FieldElement{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335}, + }, + { + FieldElement{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644}, + FieldElement{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616}, + FieldElement{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735}, + }, + { + FieldElement{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099}, + FieldElement{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341}, + FieldElement{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336}, + }, + { + FieldElement{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646}, + FieldElement{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425}, + FieldElement{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388}, + }, + { + FieldElement{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743}, + FieldElement{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822}, + FieldElement{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462}, + }, + { + FieldElement{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985}, + FieldElement{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702}, + FieldElement{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797}, + }, + { + FieldElement{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293}, + FieldElement{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100}, + FieldElement{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688}, + }, + }, + { + { + FieldElement{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186}, + FieldElement{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610}, + FieldElement{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707}, + }, + { + FieldElement{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220}, + FieldElement{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025}, + FieldElement{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044}, + }, + { + FieldElement{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992}, + FieldElement{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027}, + FieldElement{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197}, + }, + { + FieldElement{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901}, + FieldElement{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952}, + FieldElement{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878}, + }, + { + FieldElement{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390}, + FieldElement{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730}, + FieldElement{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730}, + }, + { + FieldElement{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180}, + FieldElement{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272}, + FieldElement{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715}, + }, + { + FieldElement{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970}, + FieldElement{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772}, + FieldElement{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865}, + }, + { + FieldElement{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750}, + FieldElement{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373}, + FieldElement{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348}, + }, + }, + { + { + FieldElement{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144}, + FieldElement{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195}, + FieldElement{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086}, + }, + { + FieldElement{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684}, + FieldElement{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518}, + FieldElement{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233}, + }, + { + FieldElement{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793}, + FieldElement{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794}, + FieldElement{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435}, + }, + { + FieldElement{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921}, + FieldElement{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518}, + FieldElement{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563}, + }, + { + FieldElement{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278}, + FieldElement{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024}, + FieldElement{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030}, + }, + { + FieldElement{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783}, + FieldElement{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717}, + FieldElement{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844}, + }, + { + FieldElement{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333}, + FieldElement{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048}, + FieldElement{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760}, + }, + { + FieldElement{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760}, + FieldElement{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757}, + FieldElement{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112}, + }, + }, + { + { + FieldElement{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468}, + FieldElement{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184}, + FieldElement{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289}, + }, + { + FieldElement{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066}, + FieldElement{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882}, + FieldElement{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226}, + }, + { + FieldElement{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101}, + FieldElement{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279}, + FieldElement{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811}, + }, + { + FieldElement{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709}, + FieldElement{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714}, + FieldElement{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121}, + }, + { + FieldElement{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464}, + FieldElement{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847}, + FieldElement{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400}, + }, + { + FieldElement{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414}, + FieldElement{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158}, + FieldElement{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045}, + }, + { + FieldElement{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415}, + FieldElement{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459}, + FieldElement{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079}, + }, + { + FieldElement{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412}, + FieldElement{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743}, + FieldElement{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836}, + }, + }, + { + { + FieldElement{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022}, + FieldElement{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429}, + FieldElement{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065}, + }, + { + FieldElement{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861}, + FieldElement{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000}, + FieldElement{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101}, + }, + { + FieldElement{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815}, + FieldElement{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642}, + FieldElement{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966}, + }, + { + FieldElement{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574}, + FieldElement{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742}, + FieldElement{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689}, + }, + { + FieldElement{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020}, + FieldElement{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772}, + FieldElement{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982}, + }, + { + FieldElement{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953}, + FieldElement{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218}, + FieldElement{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265}, + }, + { + FieldElement{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073}, + FieldElement{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325}, + FieldElement{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798}, + }, + { + FieldElement{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870}, + FieldElement{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863}, + FieldElement{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927}, + }, + }, + { + { + FieldElement{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267}, + FieldElement{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663}, + FieldElement{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862}, + }, + { + FieldElement{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673}, + FieldElement{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943}, + FieldElement{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020}, + }, + { + FieldElement{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238}, + FieldElement{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064}, + FieldElement{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795}, + }, + { + FieldElement{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052}, + FieldElement{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904}, + FieldElement{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531}, + }, + { + FieldElement{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979}, + FieldElement{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841}, + FieldElement{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431}, + }, + { + FieldElement{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324}, + FieldElement{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940}, + FieldElement{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320}, + }, + { + FieldElement{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184}, + FieldElement{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114}, + FieldElement{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878}, + }, + { + FieldElement{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784}, + FieldElement{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091}, + FieldElement{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585}, + }, + }, + { + { + FieldElement{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208}, + FieldElement{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864}, + FieldElement{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661}, + }, + { + FieldElement{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233}, + FieldElement{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212}, + FieldElement{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525}, + }, + { + FieldElement{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068}, + FieldElement{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397}, + FieldElement{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988}, + }, + { + FieldElement{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889}, + FieldElement{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038}, + FieldElement{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697}, + }, + { + FieldElement{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875}, + FieldElement{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905}, + FieldElement{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656}, + }, + { + FieldElement{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818}, + FieldElement{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714}, + FieldElement{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203}, + }, + { + FieldElement{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931}, + FieldElement{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024}, + FieldElement{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084}, + }, + { + FieldElement{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204}, + FieldElement{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817}, + FieldElement{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667}, + }, + }, + { + { + FieldElement{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504}, + FieldElement{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768}, + FieldElement{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255}, + }, + { + FieldElement{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790}, + FieldElement{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438}, + FieldElement{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333}, + }, + { + FieldElement{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971}, + FieldElement{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905}, + FieldElement{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409}, + }, + { + FieldElement{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409}, + FieldElement{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499}, + FieldElement{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363}, + }, + { + FieldElement{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664}, + FieldElement{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324}, + FieldElement{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940}, + }, + { + FieldElement{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990}, + FieldElement{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914}, + FieldElement{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290}, + }, + { + FieldElement{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257}, + FieldElement{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433}, + FieldElement{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236}, + }, + { + FieldElement{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045}, + FieldElement{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093}, + FieldElement{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347}, + }, + }, + { + { + FieldElement{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191}, + FieldElement{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507}, + FieldElement{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906}, + }, + { + FieldElement{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018}, + FieldElement{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109}, + FieldElement{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926}, + }, + { + FieldElement{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528}, + FieldElement{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625}, + FieldElement{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286}, + }, + { + FieldElement{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033}, + FieldElement{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866}, + FieldElement{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896}, + }, + { + FieldElement{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075}, + FieldElement{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347}, + FieldElement{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437}, + }, + { + FieldElement{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165}, + FieldElement{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588}, + FieldElement{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193}, + }, + { + FieldElement{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017}, + FieldElement{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883}, + FieldElement{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961}, + }, + { + FieldElement{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043}, + FieldElement{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663}, + FieldElement{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362}, + }, + }, + { + { + FieldElement{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860}, + FieldElement{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466}, + FieldElement{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063}, + }, + { + FieldElement{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997}, + FieldElement{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295}, + FieldElement{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369}, + }, + { + FieldElement{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385}, + FieldElement{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109}, + FieldElement{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906}, + }, + { + FieldElement{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424}, + FieldElement{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185}, + FieldElement{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962}, + }, + { + FieldElement{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325}, + FieldElement{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593}, + FieldElement{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404}, + }, + { + FieldElement{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644}, + FieldElement{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801}, + FieldElement{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804}, + }, + { + FieldElement{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884}, + FieldElement{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577}, + FieldElement{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849}, + }, + { + FieldElement{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473}, + FieldElement{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644}, + FieldElement{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319}, + }, + }, + { + { + FieldElement{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599}, + FieldElement{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768}, + FieldElement{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084}, + }, + { + FieldElement{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328}, + FieldElement{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369}, + FieldElement{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920}, + }, + { + FieldElement{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815}, + FieldElement{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025}, + FieldElement{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397}, + }, + { + FieldElement{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448}, + FieldElement{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981}, + FieldElement{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165}, + }, + { + FieldElement{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501}, + FieldElement{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073}, + FieldElement{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861}, + }, + { + FieldElement{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845}, + FieldElement{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211}, + FieldElement{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870}, + }, + { + FieldElement{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096}, + FieldElement{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803}, + FieldElement{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168}, + }, + { + FieldElement{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965}, + FieldElement{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505}, + FieldElement{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598}, + }, + }, + { + { + FieldElement{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782}, + FieldElement{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900}, + FieldElement{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479}, + }, + { + FieldElement{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208}, + FieldElement{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232}, + FieldElement{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719}, + }, + { + FieldElement{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271}, + FieldElement{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326}, + FieldElement{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132}, + }, + { + FieldElement{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300}, + FieldElement{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570}, + FieldElement{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670}, + }, + { + FieldElement{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994}, + FieldElement{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913}, + FieldElement{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317}, + }, + { + FieldElement{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730}, + FieldElement{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096}, + FieldElement{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078}, + }, + { + FieldElement{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411}, + FieldElement{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905}, + FieldElement{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654}, + }, + { + FieldElement{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870}, + FieldElement{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498}, + FieldElement{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579}, + }, + }, + { + { + FieldElement{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677}, + FieldElement{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647}, + FieldElement{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743}, + }, + { + FieldElement{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468}, + FieldElement{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375}, + FieldElement{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155}, + }, + { + FieldElement{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725}, + FieldElement{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612}, + FieldElement{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943}, + }, + { + FieldElement{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944}, + FieldElement{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928}, + FieldElement{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406}, + }, + { + FieldElement{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139}, + FieldElement{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963}, + FieldElement{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693}, + }, + { + FieldElement{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734}, + FieldElement{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680}, + FieldElement{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410}, + }, + { + FieldElement{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931}, + FieldElement{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654}, + FieldElement{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710}, + }, + { + FieldElement{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180}, + FieldElement{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684}, + FieldElement{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895}, + }, + }, + { + { + FieldElement{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501}, + FieldElement{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413}, + FieldElement{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880}, + }, + { + FieldElement{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874}, + FieldElement{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962}, + FieldElement{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899}, + }, + { + FieldElement{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152}, + FieldElement{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063}, + FieldElement{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080}, + }, + { + FieldElement{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146}, + FieldElement{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183}, + FieldElement{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133}, + }, + { + FieldElement{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421}, + FieldElement{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622}, + FieldElement{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197}, + }, + { + FieldElement{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663}, + FieldElement{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753}, + FieldElement{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755}, + }, + { + FieldElement{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862}, + FieldElement{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118}, + FieldElement{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171}, + }, + { + FieldElement{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380}, + FieldElement{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824}, + FieldElement{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270}, + }, + }, + { + { + FieldElement{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438}, + FieldElement{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584}, + FieldElement{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562}, + }, + { + FieldElement{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471}, + FieldElement{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610}, + FieldElement{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269}, + }, + { + FieldElement{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650}, + FieldElement{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369}, + FieldElement{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461}, + }, + { + FieldElement{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462}, + FieldElement{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793}, + FieldElement{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218}, + }, + { + FieldElement{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226}, + FieldElement{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019}, + FieldElement{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037}, + }, + { + FieldElement{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171}, + FieldElement{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132}, + FieldElement{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841}, + }, + { + FieldElement{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181}, + FieldElement{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210}, + FieldElement{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040}, + }, + { + FieldElement{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935}, + FieldElement{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105}, + FieldElement{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814}, + }, + }, + { + { + FieldElement{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852}, + FieldElement{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581}, + FieldElement{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646}, + }, + { + FieldElement{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844}, + FieldElement{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025}, + FieldElement{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453}, + }, + { + FieldElement{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068}, + FieldElement{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192}, + FieldElement{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921}, + }, + { + FieldElement{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259}, + FieldElement{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426}, + FieldElement{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072}, + }, + { + FieldElement{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305}, + FieldElement{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832}, + FieldElement{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943}, + }, + { + FieldElement{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011}, + FieldElement{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447}, + FieldElement{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494}, + }, + { + FieldElement{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245}, + FieldElement{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859}, + FieldElement{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915}, + }, + { + FieldElement{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707}, + FieldElement{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848}, + FieldElement{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224}, + }, + }, + { + { + FieldElement{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391}, + FieldElement{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215}, + FieldElement{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101}, + }, + { + FieldElement{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713}, + FieldElement{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849}, + FieldElement{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930}, + }, + { + FieldElement{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940}, + FieldElement{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031}, + FieldElement{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404}, + }, + { + FieldElement{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243}, + FieldElement{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116}, + FieldElement{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525}, + }, + { + FieldElement{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509}, + FieldElement{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883}, + FieldElement{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865}, + }, + { + FieldElement{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660}, + FieldElement{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273}, + FieldElement{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138}, + }, + { + FieldElement{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560}, + FieldElement{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135}, + FieldElement{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941}, + }, + { + FieldElement{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739}, + FieldElement{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756}, + FieldElement{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819}, + }, + }, + { + { + FieldElement{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347}, + FieldElement{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028}, + FieldElement{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075}, + }, + { + FieldElement{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799}, + FieldElement{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609}, + FieldElement{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817}, + }, + { + FieldElement{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989}, + FieldElement{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523}, + FieldElement{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278}, + }, + { + FieldElement{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045}, + FieldElement{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377}, + FieldElement{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480}, + }, + { + FieldElement{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016}, + FieldElement{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426}, + FieldElement{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525}, + }, + { + FieldElement{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396}, + FieldElement{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080}, + FieldElement{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892}, + }, + { + FieldElement{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275}, + FieldElement{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074}, + FieldElement{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140}, + }, + { + FieldElement{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717}, + FieldElement{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101}, + FieldElement{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127}, + }, + }, + { + { + FieldElement{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632}, + FieldElement{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415}, + FieldElement{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160}, + }, + { + FieldElement{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876}, + FieldElement{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625}, + FieldElement{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478}, + }, + { + FieldElement{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164}, + FieldElement{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595}, + FieldElement{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248}, + }, + { + FieldElement{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858}, + FieldElement{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193}, + FieldElement{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184}, + }, + { + FieldElement{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942}, + FieldElement{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635}, + FieldElement{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948}, + }, + { + FieldElement{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935}, + FieldElement{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415}, + FieldElement{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416}, + }, + { + FieldElement{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018}, + FieldElement{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778}, + FieldElement{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659}, + }, + { + FieldElement{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385}, + FieldElement{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503}, + FieldElement{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329}, + }, + }, + { + { + FieldElement{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056}, + FieldElement{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838}, + FieldElement{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948}, + }, + { + FieldElement{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691}, + FieldElement{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118}, + FieldElement{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517}, + }, + { + FieldElement{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269}, + FieldElement{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904}, + FieldElement{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589}, + }, + { + FieldElement{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193}, + FieldElement{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910}, + FieldElement{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930}, + }, + { + FieldElement{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667}, + FieldElement{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481}, + FieldElement{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876}, + }, + { + FieldElement{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640}, + FieldElement{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278}, + FieldElement{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112}, + }, + { + FieldElement{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272}, + FieldElement{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012}, + FieldElement{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221}, + }, + { + FieldElement{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046}, + FieldElement{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345}, + FieldElement{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310}, + }, + }, + { + { + FieldElement{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937}, + FieldElement{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636}, + FieldElement{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008}, + }, + { + FieldElement{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429}, + FieldElement{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576}, + FieldElement{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066}, + }, + { + FieldElement{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490}, + FieldElement{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104}, + FieldElement{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053}, + }, + { + FieldElement{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275}, + FieldElement{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511}, + FieldElement{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095}, + }, + { + FieldElement{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439}, + FieldElement{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939}, + FieldElement{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424}, + }, + { + FieldElement{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310}, + FieldElement{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608}, + FieldElement{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079}, + }, + { + FieldElement{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101}, + FieldElement{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418}, + FieldElement{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576}, + }, + { + FieldElement{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356}, + FieldElement{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996}, + FieldElement{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099}, + }, + }, + { + { + FieldElement{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728}, + FieldElement{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658}, + FieldElement{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242}, + }, + { + FieldElement{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001}, + FieldElement{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766}, + FieldElement{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373}, + }, + { + FieldElement{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458}, + FieldElement{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628}, + FieldElement{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657}, + }, + { + FieldElement{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062}, + FieldElement{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616}, + FieldElement{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014}, + }, + { + FieldElement{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383}, + FieldElement{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814}, + FieldElement{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718}, + }, + { + FieldElement{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417}, + FieldElement{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222}, + FieldElement{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444}, + }, + { + FieldElement{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597}, + FieldElement{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970}, + FieldElement{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799}, + }, + { + FieldElement{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647}, + FieldElement{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511}, + FieldElement{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032}, + }, + }, + { + { + FieldElement{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834}, + FieldElement{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461}, + FieldElement{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062}, + }, + { + FieldElement{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516}, + FieldElement{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547}, + FieldElement{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240}, + }, + { + FieldElement{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038}, + FieldElement{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741}, + FieldElement{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103}, + }, + { + FieldElement{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747}, + FieldElement{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323}, + FieldElement{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016}, + }, + { + FieldElement{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373}, + FieldElement{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228}, + FieldElement{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141}, + }, + { + FieldElement{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399}, + FieldElement{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831}, + FieldElement{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376}, + }, + { + FieldElement{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313}, + FieldElement{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958}, + FieldElement{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577}, + }, + { + FieldElement{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743}, + FieldElement{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684}, + FieldElement{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476}, + }, + }, +} diff --git a/src/vendor/github.com/agl/ed25519/edwards25519/edwards25519.go b/src/vendor/github.com/agl/ed25519/edwards25519/edwards25519.go new file mode 100644 index 000000000..907981855 --- /dev/null +++ b/src/vendor/github.com/agl/ed25519/edwards25519/edwards25519.go @@ -0,0 +1,1773 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package edwards25519 implements operations in GF(2**255-19) and on an +// Edwards curve that is isomorphic to curve25519. See +// http://ed25519.cr.yp.to/. +package edwards25519 + +// This code is a port of the public domain, "ref10" implementation of ed25519 +// from SUPERCOP. + +// FieldElement represents an element of the field GF(2^255 - 19). An element +// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 +// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on +// context. +type FieldElement [10]int32 + +var zero FieldElement + +func FeZero(fe *FieldElement) { + copy(fe[:], zero[:]) +} + +func FeOne(fe *FieldElement) { + FeZero(fe) + fe[0] = 1 +} + +func FeAdd(dst, a, b *FieldElement) { + dst[0] = a[0] + b[0] + dst[1] = a[1] + b[1] + dst[2] = a[2] + b[2] + dst[3] = a[3] + b[3] + dst[4] = a[4] + b[4] + dst[5] = a[5] + b[5] + dst[6] = a[6] + b[6] + dst[7] = a[7] + b[7] + dst[8] = a[8] + b[8] + dst[9] = a[9] + b[9] +} + +func FeSub(dst, a, b *FieldElement) { + dst[0] = a[0] - b[0] + dst[1] = a[1] - b[1] + dst[2] = a[2] - b[2] + dst[3] = a[3] - b[3] + dst[4] = a[4] - b[4] + dst[5] = a[5] - b[5] + dst[6] = a[6] - b[6] + dst[7] = a[7] - b[7] + dst[8] = a[8] - b[8] + dst[9] = a[9] - b[9] +} + +func FeCopy(dst, src *FieldElement) { + copy(dst[:], src[:]) +} + +// Replace (f,g) with (g,g) if b == 1; +// replace (f,g) with (f,g) if b == 0. +// +// Preconditions: b in {0,1}. +func FeCMove(f, g *FieldElement, b int32) { + b = -b + f[0] ^= b & (f[0] ^ g[0]) + f[1] ^= b & (f[1] ^ g[1]) + f[2] ^= b & (f[2] ^ g[2]) + f[3] ^= b & (f[3] ^ g[3]) + f[4] ^= b & (f[4] ^ g[4]) + f[5] ^= b & (f[5] ^ g[5]) + f[6] ^= b & (f[6] ^ g[6]) + f[7] ^= b & (f[7] ^ g[7]) + f[8] ^= b & (f[8] ^ g[8]) + f[9] ^= b & (f[9] ^ g[9]) +} + +func load3(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + return r +} + +func load4(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + r |= int64(in[3]) << 24 + return r +} + +func FeFromBytes(dst *FieldElement, src *[32]byte) { + h0 := load4(src[:]) + h1 := load3(src[4:]) << 6 + h2 := load3(src[7:]) << 5 + h3 := load3(src[10:]) << 3 + h4 := load3(src[13:]) << 2 + h5 := load4(src[16:]) + h6 := load3(src[20:]) << 7 + h7 := load3(src[23:]) << 5 + h8 := load3(src[26:]) << 4 + h9 := (load3(src[29:]) & 8388607) << 2 + + FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +// FeToBytes marshals h to s. +// Preconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Write p=2^255-19; q=floor(h/p). +// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). +// +// Proof: +// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. +// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. +// +// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). +// Then 0> 25 + q = (h[0] + q) >> 26 + q = (h[1] + q) >> 25 + q = (h[2] + q) >> 26 + q = (h[3] + q) >> 25 + q = (h[4] + q) >> 26 + q = (h[5] + q) >> 25 + q = (h[6] + q) >> 26 + q = (h[7] + q) >> 25 + q = (h[8] + q) >> 26 + q = (h[9] + q) >> 25 + + // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. + h[0] += 19 * q + // Goal: Output h-2^255 q, which is between 0 and 2^255-20. + + carry[0] = h[0] >> 26 + h[1] += carry[0] + h[0] -= carry[0] << 26 + carry[1] = h[1] >> 25 + h[2] += carry[1] + h[1] -= carry[1] << 25 + carry[2] = h[2] >> 26 + h[3] += carry[2] + h[2] -= carry[2] << 26 + carry[3] = h[3] >> 25 + h[4] += carry[3] + h[3] -= carry[3] << 25 + carry[4] = h[4] >> 26 + h[5] += carry[4] + h[4] -= carry[4] << 26 + carry[5] = h[5] >> 25 + h[6] += carry[5] + h[5] -= carry[5] << 25 + carry[6] = h[6] >> 26 + h[7] += carry[6] + h[6] -= carry[6] << 26 + carry[7] = h[7] >> 25 + h[8] += carry[7] + h[7] -= carry[7] << 25 + carry[8] = h[8] >> 26 + h[9] += carry[8] + h[8] -= carry[8] << 26 + carry[9] = h[9] >> 25 + h[9] -= carry[9] << 25 + // h10 = carry9 + + // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; + // evidently 2^255 h10-2^255 q = 0. + // Goal: Output h[0]+...+2^230 h[9]. + + s[0] = byte(h[0] >> 0) + s[1] = byte(h[0] >> 8) + s[2] = byte(h[0] >> 16) + s[3] = byte((h[0] >> 24) | (h[1] << 2)) + s[4] = byte(h[1] >> 6) + s[5] = byte(h[1] >> 14) + s[6] = byte((h[1] >> 22) | (h[2] << 3)) + s[7] = byte(h[2] >> 5) + s[8] = byte(h[2] >> 13) + s[9] = byte((h[2] >> 21) | (h[3] << 5)) + s[10] = byte(h[3] >> 3) + s[11] = byte(h[3] >> 11) + s[12] = byte((h[3] >> 19) | (h[4] << 6)) + s[13] = byte(h[4] >> 2) + s[14] = byte(h[4] >> 10) + s[15] = byte(h[4] >> 18) + s[16] = byte(h[5] >> 0) + s[17] = byte(h[5] >> 8) + s[18] = byte(h[5] >> 16) + s[19] = byte((h[5] >> 24) | (h[6] << 1)) + s[20] = byte(h[6] >> 7) + s[21] = byte(h[6] >> 15) + s[22] = byte((h[6] >> 23) | (h[7] << 3)) + s[23] = byte(h[7] >> 5) + s[24] = byte(h[7] >> 13) + s[25] = byte((h[7] >> 21) | (h[8] << 4)) + s[26] = byte(h[8] >> 4) + s[27] = byte(h[8] >> 12) + s[28] = byte((h[8] >> 20) | (h[9] << 6)) + s[29] = byte(h[9] >> 2) + s[30] = byte(h[9] >> 10) + s[31] = byte(h[9] >> 18) +} + +func FeIsNegative(f *FieldElement) byte { + var s [32]byte + FeToBytes(&s, f) + return s[0] & 1 +} + +func FeIsNonZero(f *FieldElement) int32 { + var s [32]byte + FeToBytes(&s, f) + var x uint8 + for _, b := range s { + x |= b + } + x |= x >> 4 + x |= x >> 2 + x |= x >> 1 + return int32(x & 1) +} + +// FeNeg sets h = -f +// +// Preconditions: +// |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func FeNeg(h, f *FieldElement) { + h[0] = -f[0] + h[1] = -f[1] + h[2] = -f[2] + h[3] = -f[3] + h[4] = -f[4] + h[5] = -f[5] + h[6] = -f[6] + h[7] = -f[7] + h[8] = -f[8] + h[9] = -f[9] +} + +func FeCombine(h *FieldElement, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) { + var c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 int64 + + /* + |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) + i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 + |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) + i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 + */ + + c0 = (h0 + (1 << 25)) >> 26 + h1 += c0 + h0 -= c0 << 26 + c4 = (h4 + (1 << 25)) >> 26 + h5 += c4 + h4 -= c4 << 26 + /* |h0| <= 2^25 */ + /* |h4| <= 2^25 */ + /* |h1| <= 1.51*2^58 */ + /* |h5| <= 1.51*2^58 */ + + c1 = (h1 + (1 << 24)) >> 25 + h2 += c1 + h1 -= c1 << 25 + c5 = (h5 + (1 << 24)) >> 25 + h6 += c5 + h5 -= c5 << 25 + /* |h1| <= 2^24; from now on fits into int32 */ + /* |h5| <= 2^24; from now on fits into int32 */ + /* |h2| <= 1.21*2^59 */ + /* |h6| <= 1.21*2^59 */ + + c2 = (h2 + (1 << 25)) >> 26 + h3 += c2 + h2 -= c2 << 26 + c6 = (h6 + (1 << 25)) >> 26 + h7 += c6 + h6 -= c6 << 26 + /* |h2| <= 2^25; from now on fits into int32 unchanged */ + /* |h6| <= 2^25; from now on fits into int32 unchanged */ + /* |h3| <= 1.51*2^58 */ + /* |h7| <= 1.51*2^58 */ + + c3 = (h3 + (1 << 24)) >> 25 + h4 += c3 + h3 -= c3 << 25 + c7 = (h7 + (1 << 24)) >> 25 + h8 += c7 + h7 -= c7 << 25 + /* |h3| <= 2^24; from now on fits into int32 unchanged */ + /* |h7| <= 2^24; from now on fits into int32 unchanged */ + /* |h4| <= 1.52*2^33 */ + /* |h8| <= 1.52*2^33 */ + + c4 = (h4 + (1 << 25)) >> 26 + h5 += c4 + h4 -= c4 << 26 + c8 = (h8 + (1 << 25)) >> 26 + h9 += c8 + h8 -= c8 << 26 + /* |h4| <= 2^25; from now on fits into int32 unchanged */ + /* |h8| <= 2^25; from now on fits into int32 unchanged */ + /* |h5| <= 1.01*2^24 */ + /* |h9| <= 1.51*2^58 */ + + c9 = (h9 + (1 << 24)) >> 25 + h0 += c9 * 19 + h9 -= c9 << 25 + /* |h9| <= 2^24; from now on fits into int32 unchanged */ + /* |h0| <= 1.8*2^37 */ + + c0 = (h0 + (1 << 25)) >> 26 + h1 += c0 + h0 -= c0 << 26 + /* |h0| <= 2^25; from now on fits into int32 unchanged */ + /* |h1| <= 1.01*2^24 */ + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// FeMul calculates h = f * g +// Can overlap h with f or g. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Notes on implementation strategy: +// +// Using schoolbook multiplication. +// Karatsuba would save a little in some cost models. +// +// Most multiplications by 2 and 19 are 32-bit precomputations; +// cheaper than 64-bit postcomputations. +// +// There is one remaining multiplication by 19 in the carry chain; +// one *19 precomputation can be merged into this, +// but the resulting data flow is considerably less clean. +// +// There are 12 carries below. +// 10 of them are 2-way parallelizable and vectorizable. +// Can get away with 11 carries, but then data flow is much deeper. +// +// With tighter constraints on inputs can squeeze carries into int32. +func FeMul(h, f, g *FieldElement) { + f0 := int64(f[0]) + f1 := int64(f[1]) + f2 := int64(f[2]) + f3 := int64(f[3]) + f4 := int64(f[4]) + f5 := int64(f[5]) + f6 := int64(f[6]) + f7 := int64(f[7]) + f8 := int64(f[8]) + f9 := int64(f[9]) + + f1_2 := int64(2 * f[1]) + f3_2 := int64(2 * f[3]) + f5_2 := int64(2 * f[5]) + f7_2 := int64(2 * f[7]) + f9_2 := int64(2 * f[9]) + + g0 := int64(g[0]) + g1 := int64(g[1]) + g2 := int64(g[2]) + g3 := int64(g[3]) + g4 := int64(g[4]) + g5 := int64(g[5]) + g6 := int64(g[6]) + g7 := int64(g[7]) + g8 := int64(g[8]) + g9 := int64(g[9]) + + g1_19 := int64(19 * g[1]) /* 1.4*2^29 */ + g2_19 := int64(19 * g[2]) /* 1.4*2^30; still ok */ + g3_19 := int64(19 * g[3]) + g4_19 := int64(19 * g[4]) + g5_19 := int64(19 * g[5]) + g6_19 := int64(19 * g[6]) + g7_19 := int64(19 * g[7]) + g8_19 := int64(19 * g[8]) + g9_19 := int64(19 * g[9]) + + h0 := f0*g0 + f1_2*g9_19 + f2*g8_19 + f3_2*g7_19 + f4*g6_19 + f5_2*g5_19 + f6*g4_19 + f7_2*g3_19 + f8*g2_19 + f9_2*g1_19 + h1 := f0*g1 + f1*g0 + f2*g9_19 + f3*g8_19 + f4*g7_19 + f5*g6_19 + f6*g5_19 + f7*g4_19 + f8*g3_19 + f9*g2_19 + h2 := f0*g2 + f1_2*g1 + f2*g0 + f3_2*g9_19 + f4*g8_19 + f5_2*g7_19 + f6*g6_19 + f7_2*g5_19 + f8*g4_19 + f9_2*g3_19 + h3 := f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4*g9_19 + f5*g8_19 + f6*g7_19 + f7*g6_19 + f8*g5_19 + f9*g4_19 + h4 := f0*g4 + f1_2*g3 + f2*g2 + f3_2*g1 + f4*g0 + f5_2*g9_19 + f6*g8_19 + f7_2*g7_19 + f8*g6_19 + f9_2*g5_19 + h5 := f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 + f5*g0 + f6*g9_19 + f7*g8_19 + f8*g7_19 + f9*g6_19 + h6 := f0*g6 + f1_2*g5 + f2*g4 + f3_2*g3 + f4*g2 + f5_2*g1 + f6*g0 + f7_2*g9_19 + f8*g8_19 + f9_2*g7_19 + h7 := f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 + f5*g2 + f6*g1 + f7*g0 + f8*g9_19 + f9*g8_19 + h8 := f0*g8 + f1_2*g7 + f2*g6 + f3_2*g5 + f4*g4 + f5_2*g3 + f6*g2 + f7_2*g1 + f8*g0 + f9_2*g9_19 + h9 := f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 + f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0 + + FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +func feSquare(f *FieldElement) (h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) { + f0 := int64(f[0]) + f1 := int64(f[1]) + f2 := int64(f[2]) + f3 := int64(f[3]) + f4 := int64(f[4]) + f5 := int64(f[5]) + f6 := int64(f[6]) + f7 := int64(f[7]) + f8 := int64(f[8]) + f9 := int64(f[9]) + f0_2 := int64(2 * f[0]) + f1_2 := int64(2 * f[1]) + f2_2 := int64(2 * f[2]) + f3_2 := int64(2 * f[3]) + f4_2 := int64(2 * f[4]) + f5_2 := int64(2 * f[5]) + f6_2 := int64(2 * f[6]) + f7_2 := int64(2 * f[7]) + f5_38 := 38 * f5 // 1.31*2^30 + f6_19 := 19 * f6 // 1.31*2^30 + f7_38 := 38 * f7 // 1.31*2^30 + f8_19 := 19 * f8 // 1.31*2^30 + f9_38 := 38 * f9 // 1.31*2^30 + + h0 = f0*f0 + f1_2*f9_38 + f2_2*f8_19 + f3_2*f7_38 + f4_2*f6_19 + f5*f5_38 + h1 = f0_2*f1 + f2*f9_38 + f3_2*f8_19 + f4*f7_38 + f5_2*f6_19 + h2 = f0_2*f2 + f1_2*f1 + f3_2*f9_38 + f4_2*f8_19 + f5_2*f7_38 + f6*f6_19 + h3 = f0_2*f3 + f1_2*f2 + f4*f9_38 + f5_2*f8_19 + f6*f7_38 + h4 = f0_2*f4 + f1_2*f3_2 + f2*f2 + f5_2*f9_38 + f6_2*f8_19 + f7*f7_38 + h5 = f0_2*f5 + f1_2*f4 + f2_2*f3 + f6*f9_38 + f7_2*f8_19 + h6 = f0_2*f6 + f1_2*f5_2 + f2_2*f4 + f3_2*f3 + f7_2*f9_38 + f8*f8_19 + h7 = f0_2*f7 + f1_2*f6 + f2_2*f5 + f3_2*f4 + f8*f9_38 + h8 = f0_2*f8 + f1_2*f7_2 + f2_2*f6 + f3_2*f5_2 + f4*f4 + f9*f9_38 + h9 = f0_2*f9 + f1_2*f8 + f2_2*f7 + f3_2*f6 + f4_2*f5 + + return +} + +// FeSquare calculates h = f*f. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func FeSquare(h, f *FieldElement) { + h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f) + FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +// FeSquare2 sets h = 2 * f * f +// +// Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +// See fe_mul.c for discussion of implementation strategy. +func FeSquare2(h, f *FieldElement) { + h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f) + + h0 += h0 + h1 += h1 + h2 += h2 + h3 += h3 + h4 += h4 + h5 += h5 + h6 += h6 + h7 += h7 + h8 += h8 + h9 += h9 + + FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9) +} + +func FeInvert(out, z *FieldElement) { + var t0, t1, t2, t3 FieldElement + var i int + + FeSquare(&t0, z) // 2^1 + FeSquare(&t1, &t0) // 2^2 + for i = 1; i < 2; i++ { // 2^3 + FeSquare(&t1, &t1) + } + FeMul(&t1, z, &t1) // 2^3 + 2^0 + FeMul(&t0, &t0, &t1) // 2^3 + 2^1 + 2^0 + FeSquare(&t2, &t0) // 2^4 + 2^2 + 2^1 + FeMul(&t1, &t1, &t2) // 2^4 + 2^3 + 2^2 + 2^1 + 2^0 + FeSquare(&t2, &t1) // 5,4,3,2,1 + for i = 1; i < 5; i++ { // 9,8,7,6,5 + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0 + FeSquare(&t2, &t1) // 10..1 + for i = 1; i < 10; i++ { // 19..10 + FeSquare(&t2, &t2) + } + FeMul(&t2, &t2, &t1) // 19..0 + FeSquare(&t3, &t2) // 20..1 + for i = 1; i < 20; i++ { // 39..20 + FeSquare(&t3, &t3) + } + FeMul(&t2, &t3, &t2) // 39..0 + FeSquare(&t2, &t2) // 40..1 + for i = 1; i < 10; i++ { // 49..10 + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) // 49..0 + FeSquare(&t2, &t1) // 50..1 + for i = 1; i < 50; i++ { // 99..50 + FeSquare(&t2, &t2) + } + FeMul(&t2, &t2, &t1) // 99..0 + FeSquare(&t3, &t2) // 100..1 + for i = 1; i < 100; i++ { // 199..100 + FeSquare(&t3, &t3) + } + FeMul(&t2, &t3, &t2) // 199..0 + FeSquare(&t2, &t2) // 200..1 + for i = 1; i < 50; i++ { // 249..50 + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) // 249..0 + FeSquare(&t1, &t1) // 250..1 + for i = 1; i < 5; i++ { // 254..5 + FeSquare(&t1, &t1) + } + FeMul(out, &t1, &t0) // 254..5,3,1,0 +} + +func fePow22523(out, z *FieldElement) { + var t0, t1, t2 FieldElement + var i int + + FeSquare(&t0, z) + for i = 1; i < 1; i++ { + FeSquare(&t0, &t0) + } + FeSquare(&t1, &t0) + for i = 1; i < 2; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t1, z, &t1) + FeMul(&t0, &t0, &t1) + FeSquare(&t0, &t0) + for i = 1; i < 1; i++ { + FeSquare(&t0, &t0) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t1, &t0) + for i = 1; i < 5; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t1, &t0) + for i = 1; i < 10; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t1, &t1, &t0) + FeSquare(&t2, &t1) + for i = 1; i < 20; i++ { + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) + FeSquare(&t1, &t1) + for i = 1; i < 10; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t1, &t0) + for i = 1; i < 50; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t1, &t1, &t0) + FeSquare(&t2, &t1) + for i = 1; i < 100; i++ { + FeSquare(&t2, &t2) + } + FeMul(&t1, &t2, &t1) + FeSquare(&t1, &t1) + for i = 1; i < 50; i++ { + FeSquare(&t1, &t1) + } + FeMul(&t0, &t1, &t0) + FeSquare(&t0, &t0) + for i = 1; i < 2; i++ { + FeSquare(&t0, &t0) + } + FeMul(out, &t0, z) +} + +// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 * +// y^2 where d = -121665/121666. +// +// Several representations are used: +// ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z +// ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT +// CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T +// PreComputedGroupElement: (y+x,y-x,2dxy) + +type ProjectiveGroupElement struct { + X, Y, Z FieldElement +} + +type ExtendedGroupElement struct { + X, Y, Z, T FieldElement +} + +type CompletedGroupElement struct { + X, Y, Z, T FieldElement +} + +type PreComputedGroupElement struct { + yPlusX, yMinusX, xy2d FieldElement +} + +type CachedGroupElement struct { + yPlusX, yMinusX, Z, T2d FieldElement +} + +func (p *ProjectiveGroupElement) Zero() { + FeZero(&p.X) + FeOne(&p.Y) + FeOne(&p.Z) +} + +func (p *ProjectiveGroupElement) Double(r *CompletedGroupElement) { + var t0 FieldElement + + FeSquare(&r.X, &p.X) + FeSquare(&r.Z, &p.Y) + FeSquare2(&r.T, &p.Z) + FeAdd(&r.Y, &p.X, &p.Y) + FeSquare(&t0, &r.Y) + FeAdd(&r.Y, &r.Z, &r.X) + FeSub(&r.Z, &r.Z, &r.X) + FeSub(&r.X, &t0, &r.Y) + FeSub(&r.T, &r.T, &r.Z) +} + +func (p *ProjectiveGroupElement) ToBytes(s *[32]byte) { + var recip, x, y FieldElement + + FeInvert(&recip, &p.Z) + FeMul(&x, &p.X, &recip) + FeMul(&y, &p.Y, &recip) + FeToBytes(s, &y) + s[31] ^= FeIsNegative(&x) << 7 +} + +func (p *ExtendedGroupElement) Zero() { + FeZero(&p.X) + FeOne(&p.Y) + FeOne(&p.Z) + FeZero(&p.T) +} + +func (p *ExtendedGroupElement) Double(r *CompletedGroupElement) { + var q ProjectiveGroupElement + p.ToProjective(&q) + q.Double(r) +} + +func (p *ExtendedGroupElement) ToCached(r *CachedGroupElement) { + FeAdd(&r.yPlusX, &p.Y, &p.X) + FeSub(&r.yMinusX, &p.Y, &p.X) + FeCopy(&r.Z, &p.Z) + FeMul(&r.T2d, &p.T, &d2) +} + +func (p *ExtendedGroupElement) ToProjective(r *ProjectiveGroupElement) { + FeCopy(&r.X, &p.X) + FeCopy(&r.Y, &p.Y) + FeCopy(&r.Z, &p.Z) +} + +func (p *ExtendedGroupElement) ToBytes(s *[32]byte) { + var recip, x, y FieldElement + + FeInvert(&recip, &p.Z) + FeMul(&x, &p.X, &recip) + FeMul(&y, &p.Y, &recip) + FeToBytes(s, &y) + s[31] ^= FeIsNegative(&x) << 7 +} + +func (p *ExtendedGroupElement) FromBytes(s *[32]byte) bool { + var u, v, v3, vxx, check FieldElement + + FeFromBytes(&p.Y, s) + FeOne(&p.Z) + FeSquare(&u, &p.Y) + FeMul(&v, &u, &d) + FeSub(&u, &u, &p.Z) // y = y^2-1 + FeAdd(&v, &v, &p.Z) // v = dy^2+1 + + FeSquare(&v3, &v) + FeMul(&v3, &v3, &v) // v3 = v^3 + FeSquare(&p.X, &v3) + FeMul(&p.X, &p.X, &v) + FeMul(&p.X, &p.X, &u) // x = uv^7 + + fePow22523(&p.X, &p.X) // x = (uv^7)^((q-5)/8) + FeMul(&p.X, &p.X, &v3) + FeMul(&p.X, &p.X, &u) // x = uv^3(uv^7)^((q-5)/8) + + var tmpX, tmp2 [32]byte + + FeSquare(&vxx, &p.X) + FeMul(&vxx, &vxx, &v) + FeSub(&check, &vxx, &u) // vx^2-u + if FeIsNonZero(&check) == 1 { + FeAdd(&check, &vxx, &u) // vx^2+u + if FeIsNonZero(&check) == 1 { + return false + } + FeMul(&p.X, &p.X, &SqrtM1) + + FeToBytes(&tmpX, &p.X) + for i, v := range tmpX { + tmp2[31-i] = v + } + } + + if FeIsNegative(&p.X) != (s[31] >> 7) { + FeNeg(&p.X, &p.X) + } + + FeMul(&p.T, &p.X, &p.Y) + return true +} + +func (p *CompletedGroupElement) ToProjective(r *ProjectiveGroupElement) { + FeMul(&r.X, &p.X, &p.T) + FeMul(&r.Y, &p.Y, &p.Z) + FeMul(&r.Z, &p.Z, &p.T) +} + +func (p *CompletedGroupElement) ToExtended(r *ExtendedGroupElement) { + FeMul(&r.X, &p.X, &p.T) + FeMul(&r.Y, &p.Y, &p.Z) + FeMul(&r.Z, &p.Z, &p.T) + FeMul(&r.T, &p.X, &p.Y) +} + +func (p *PreComputedGroupElement) Zero() { + FeOne(&p.yPlusX) + FeOne(&p.yMinusX) + FeZero(&p.xy2d) +} + +func geAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yPlusX) + FeMul(&r.Y, &r.Y, &q.yMinusX) + FeMul(&r.T, &q.T2d, &p.T) + FeMul(&r.X, &p.Z, &q.Z) + FeAdd(&t0, &r.X, &r.X) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeAdd(&r.Z, &t0, &r.T) + FeSub(&r.T, &t0, &r.T) +} + +func geSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yMinusX) + FeMul(&r.Y, &r.Y, &q.yPlusX) + FeMul(&r.T, &q.T2d, &p.T) + FeMul(&r.X, &p.Z, &q.Z) + FeAdd(&t0, &r.X, &r.X) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeSub(&r.Z, &t0, &r.T) + FeAdd(&r.T, &t0, &r.T) +} + +func geMixedAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yPlusX) + FeMul(&r.Y, &r.Y, &q.yMinusX) + FeMul(&r.T, &q.xy2d, &p.T) + FeAdd(&t0, &p.Z, &p.Z) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeAdd(&r.Z, &t0, &r.T) + FeSub(&r.T, &t0, &r.T) +} + +func geMixedSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) { + var t0 FieldElement + + FeAdd(&r.X, &p.Y, &p.X) + FeSub(&r.Y, &p.Y, &p.X) + FeMul(&r.Z, &r.X, &q.yMinusX) + FeMul(&r.Y, &r.Y, &q.yPlusX) + FeMul(&r.T, &q.xy2d, &p.T) + FeAdd(&t0, &p.Z, &p.Z) + FeSub(&r.X, &r.Z, &r.Y) + FeAdd(&r.Y, &r.Z, &r.Y) + FeSub(&r.Z, &t0, &r.T) + FeAdd(&r.T, &t0, &r.T) +} + +func slide(r *[256]int8, a *[32]byte) { + for i := range r { + r[i] = int8(1 & (a[i>>3] >> uint(i&7))) + } + + for i := range r { + if r[i] != 0 { + for b := 1; b <= 6 && i+b < 256; b++ { + if r[i+b] != 0 { + if r[i]+(r[i+b]<= -15 { + r[i] -= r[i+b] << uint(b) + for k := i + b; k < 256; k++ { + if r[k] == 0 { + r[k] = 1 + break + } + r[k] = 0 + } + } else { + break + } + } + } + } + } +} + +// GeDoubleScalarMultVartime sets r = a*A + b*B +// where a = a[0]+256*a[1]+...+256^31 a[31]. +// and b = b[0]+256*b[1]+...+256^31 b[31]. +// B is the Ed25519 base point (x,4/5) with x positive. +func GeDoubleScalarMultVartime(r *ProjectiveGroupElement, a *[32]byte, A *ExtendedGroupElement, b *[32]byte) { + var aSlide, bSlide [256]int8 + var Ai [8]CachedGroupElement // A,3A,5A,7A,9A,11A,13A,15A + var t CompletedGroupElement + var u, A2 ExtendedGroupElement + var i int + + slide(&aSlide, a) + slide(&bSlide, b) + + A.ToCached(&Ai[0]) + A.Double(&t) + t.ToExtended(&A2) + + for i := 0; i < 7; i++ { + geAdd(&t, &A2, &Ai[i]) + t.ToExtended(&u) + u.ToCached(&Ai[i+1]) + } + + r.Zero() + + for i = 255; i >= 0; i-- { + if aSlide[i] != 0 || bSlide[i] != 0 { + break + } + } + + for ; i >= 0; i-- { + r.Double(&t) + + if aSlide[i] > 0 { + t.ToExtended(&u) + geAdd(&t, &u, &Ai[aSlide[i]/2]) + } else if aSlide[i] < 0 { + t.ToExtended(&u) + geSub(&t, &u, &Ai[(-aSlide[i])/2]) + } + + if bSlide[i] > 0 { + t.ToExtended(&u) + geMixedAdd(&t, &u, &bi[bSlide[i]/2]) + } else if bSlide[i] < 0 { + t.ToExtended(&u) + geMixedSub(&t, &u, &bi[(-bSlide[i])/2]) + } + + t.ToProjective(r) + } +} + +// equal returns 1 if b == c and 0 otherwise. +func equal(b, c int32) int32 { + x := uint32(b ^ c) + x-- + return int32(x >> 31) +} + +// negative returns 1 if b < 0 and 0 otherwise. +func negative(b int32) int32 { + return (b >> 31) & 1 +} + +func PreComputedGroupElementCMove(t, u *PreComputedGroupElement, b int32) { + FeCMove(&t.yPlusX, &u.yPlusX, b) + FeCMove(&t.yMinusX, &u.yMinusX, b) + FeCMove(&t.xy2d, &u.xy2d, b) +} + +func selectPoint(t *PreComputedGroupElement, pos int32, b int32) { + var minusT PreComputedGroupElement + bNegative := negative(b) + bAbs := b - (((-bNegative) & b) << 1) + + t.Zero() + for i := int32(0); i < 8; i++ { + PreComputedGroupElementCMove(t, &base[pos][i], equal(bAbs, i+1)) + } + FeCopy(&minusT.yPlusX, &t.yMinusX) + FeCopy(&minusT.yMinusX, &t.yPlusX) + FeNeg(&minusT.xy2d, &t.xy2d) + PreComputedGroupElementCMove(t, &minusT, bNegative) +} + +// GeScalarMultBase computes h = a*B, where +// a = a[0]+256*a[1]+...+256^31 a[31] +// B is the Ed25519 base point (x,4/5) with x positive. +// +// Preconditions: +// a[31] <= 127 +func GeScalarMultBase(h *ExtendedGroupElement, a *[32]byte) { + var e [64]int8 + + for i, v := range a { + e[2*i] = int8(v & 15) + e[2*i+1] = int8((v >> 4) & 15) + } + + // each e[i] is between 0 and 15 and e[63] is between 0 and 7. + + carry := int8(0) + for i := 0; i < 63; i++ { + e[i] += carry + carry = (e[i] + 8) >> 4 + e[i] -= carry << 4 + } + e[63] += carry + // each e[i] is between -8 and 8. + + h.Zero() + var t PreComputedGroupElement + var r CompletedGroupElement + for i := int32(1); i < 64; i += 2 { + selectPoint(&t, i/2, int32(e[i])) + geMixedAdd(&r, h, &t) + r.ToExtended(h) + } + + var s ProjectiveGroupElement + + h.Double(&r) + r.ToProjective(&s) + s.Double(&r) + r.ToProjective(&s) + s.Double(&r) + r.ToProjective(&s) + s.Double(&r) + r.ToExtended(h) + + for i := int32(0); i < 64; i += 2 { + selectPoint(&t, i/2, int32(e[i])) + geMixedAdd(&r, h, &t) + r.ToExtended(h) + } +} + +// The scalars are GF(2^252 + 27742317777372353535851937790883648493). + +// Input: +// a[0]+256*a[1]+...+256^31*a[31] = a +// b[0]+256*b[1]+...+256^31*b[31] = b +// c[0]+256*c[1]+...+256^31*c[31] = c +// +// Output: +// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l +// where l = 2^252 + 27742317777372353535851937790883648493. +func ScMulAdd(s, a, b, c *[32]byte) { + a0 := 2097151 & load3(a[:]) + a1 := 2097151 & (load4(a[2:]) >> 5) + a2 := 2097151 & (load3(a[5:]) >> 2) + a3 := 2097151 & (load4(a[7:]) >> 7) + a4 := 2097151 & (load4(a[10:]) >> 4) + a5 := 2097151 & (load3(a[13:]) >> 1) + a6 := 2097151 & (load4(a[15:]) >> 6) + a7 := 2097151 & (load3(a[18:]) >> 3) + a8 := 2097151 & load3(a[21:]) + a9 := 2097151 & (load4(a[23:]) >> 5) + a10 := 2097151 & (load3(a[26:]) >> 2) + a11 := (load4(a[28:]) >> 7) + b0 := 2097151 & load3(b[:]) + b1 := 2097151 & (load4(b[2:]) >> 5) + b2 := 2097151 & (load3(b[5:]) >> 2) + b3 := 2097151 & (load4(b[7:]) >> 7) + b4 := 2097151 & (load4(b[10:]) >> 4) + b5 := 2097151 & (load3(b[13:]) >> 1) + b6 := 2097151 & (load4(b[15:]) >> 6) + b7 := 2097151 & (load3(b[18:]) >> 3) + b8 := 2097151 & load3(b[21:]) + b9 := 2097151 & (load4(b[23:]) >> 5) + b10 := 2097151 & (load3(b[26:]) >> 2) + b11 := (load4(b[28:]) >> 7) + c0 := 2097151 & load3(c[:]) + c1 := 2097151 & (load4(c[2:]) >> 5) + c2 := 2097151 & (load3(c[5:]) >> 2) + c3 := 2097151 & (load4(c[7:]) >> 7) + c4 := 2097151 & (load4(c[10:]) >> 4) + c5 := 2097151 & (load3(c[13:]) >> 1) + c6 := 2097151 & (load4(c[15:]) >> 6) + c7 := 2097151 & (load3(c[18:]) >> 3) + c8 := 2097151 & load3(c[21:]) + c9 := 2097151 & (load4(c[23:]) >> 5) + c10 := 2097151 & (load3(c[26:]) >> 2) + c11 := (load4(c[28:]) >> 7) + var carry [23]int64 + + s0 := c0 + a0*b0 + s1 := c1 + a0*b1 + a1*b0 + s2 := c2 + a0*b2 + a1*b1 + a2*b0 + s3 := c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0 + s4 := c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0 + s5 := c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0 + s6 := c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0 + s7 := c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0 + s8 := c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0 + s9 := c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0 + s10 := c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0 + s11 := c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0 + s12 := a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1 + s13 := a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2 + s14 := a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3 + s15 := a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4 + s16 := a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5 + s17 := a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6 + s18 := a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7 + s19 := a8*b11 + a9*b10 + a10*b9 + a11*b8 + s20 := a9*b11 + a10*b10 + a11*b9 + s21 := a10*b11 + a11*b10 + s22 := a11 * b11 + s23 := int64(0) + + carry[0] = (s0 + (1 << 20)) >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[2] = (s2 + (1 << 20)) >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[4] = (s4 + (1 << 20)) >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[12] = (s12 + (1 << 20)) >> 21 + s13 += carry[12] + s12 -= carry[12] << 21 + carry[14] = (s14 + (1 << 20)) >> 21 + s15 += carry[14] + s14 -= carry[14] << 21 + carry[16] = (s16 + (1 << 20)) >> 21 + s17 += carry[16] + s16 -= carry[16] << 21 + carry[18] = (s18 + (1 << 20)) >> 21 + s19 += carry[18] + s18 -= carry[18] << 21 + carry[20] = (s20 + (1 << 20)) >> 21 + s21 += carry[20] + s20 -= carry[20] << 21 + carry[22] = (s22 + (1 << 20)) >> 21 + s23 += carry[22] + s22 -= carry[22] << 21 + + carry[1] = (s1 + (1 << 20)) >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[3] = (s3 + (1 << 20)) >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[5] = (s5 + (1 << 20)) >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + carry[13] = (s13 + (1 << 20)) >> 21 + s14 += carry[13] + s13 -= carry[13] << 21 + carry[15] = (s15 + (1 << 20)) >> 21 + s16 += carry[15] + s15 -= carry[15] << 21 + carry[17] = (s17 + (1 << 20)) >> 21 + s18 += carry[17] + s17 -= carry[17] << 21 + carry[19] = (s19 + (1 << 20)) >> 21 + s20 += carry[19] + s19 -= carry[19] << 21 + carry[21] = (s21 + (1 << 20)) >> 21 + s22 += carry[21] + s21 -= carry[21] << 21 + + s11 += s23 * 666643 + s12 += s23 * 470296 + s13 += s23 * 654183 + s14 -= s23 * 997805 + s15 += s23 * 136657 + s16 -= s23 * 683901 + s23 = 0 + + s10 += s22 * 666643 + s11 += s22 * 470296 + s12 += s22 * 654183 + s13 -= s22 * 997805 + s14 += s22 * 136657 + s15 -= s22 * 683901 + s22 = 0 + + s9 += s21 * 666643 + s10 += s21 * 470296 + s11 += s21 * 654183 + s12 -= s21 * 997805 + s13 += s21 * 136657 + s14 -= s21 * 683901 + s21 = 0 + + s8 += s20 * 666643 + s9 += s20 * 470296 + s10 += s20 * 654183 + s11 -= s20 * 997805 + s12 += s20 * 136657 + s13 -= s20 * 683901 + s20 = 0 + + s7 += s19 * 666643 + s8 += s19 * 470296 + s9 += s19 * 654183 + s10 -= s19 * 997805 + s11 += s19 * 136657 + s12 -= s19 * 683901 + s19 = 0 + + s6 += s18 * 666643 + s7 += s18 * 470296 + s8 += s18 * 654183 + s9 -= s18 * 997805 + s10 += s18 * 136657 + s11 -= s18 * 683901 + s18 = 0 + + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[12] = (s12 + (1 << 20)) >> 21 + s13 += carry[12] + s12 -= carry[12] << 21 + carry[14] = (s14 + (1 << 20)) >> 21 + s15 += carry[14] + s14 -= carry[14] << 21 + carry[16] = (s16 + (1 << 20)) >> 21 + s17 += carry[16] + s16 -= carry[16] << 21 + + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + carry[13] = (s13 + (1 << 20)) >> 21 + s14 += carry[13] + s13 -= carry[13] << 21 + carry[15] = (s15 + (1 << 20)) >> 21 + s16 += carry[15] + s15 -= carry[15] << 21 + + s5 += s17 * 666643 + s6 += s17 * 470296 + s7 += s17 * 654183 + s8 -= s17 * 997805 + s9 += s17 * 136657 + s10 -= s17 * 683901 + s17 = 0 + + s4 += s16 * 666643 + s5 += s16 * 470296 + s6 += s16 * 654183 + s7 -= s16 * 997805 + s8 += s16 * 136657 + s9 -= s16 * 683901 + s16 = 0 + + s3 += s15 * 666643 + s4 += s15 * 470296 + s5 += s15 * 654183 + s6 -= s15 * 997805 + s7 += s15 * 136657 + s8 -= s15 * 683901 + s15 = 0 + + s2 += s14 * 666643 + s3 += s14 * 470296 + s4 += s14 * 654183 + s5 -= s14 * 997805 + s6 += s14 * 136657 + s7 -= s14 * 683901 + s14 = 0 + + s1 += s13 * 666643 + s2 += s13 * 470296 + s3 += s13 * 654183 + s4 -= s13 * 997805 + s5 += s13 * 136657 + s6 -= s13 * 683901 + s13 = 0 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = (s0 + (1 << 20)) >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[2] = (s2 + (1 << 20)) >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[4] = (s4 + (1 << 20)) >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + carry[1] = (s1 + (1 << 20)) >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[3] = (s3 + (1 << 20)) >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[5] = (s5 + (1 << 20)) >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[11] = s11 >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + s[0] = byte(s0 >> 0) + s[1] = byte(s0 >> 8) + s[2] = byte((s0 >> 16) | (s1 << 5)) + s[3] = byte(s1 >> 3) + s[4] = byte(s1 >> 11) + s[5] = byte((s1 >> 19) | (s2 << 2)) + s[6] = byte(s2 >> 6) + s[7] = byte((s2 >> 14) | (s3 << 7)) + s[8] = byte(s3 >> 1) + s[9] = byte(s3 >> 9) + s[10] = byte((s3 >> 17) | (s4 << 4)) + s[11] = byte(s4 >> 4) + s[12] = byte(s4 >> 12) + s[13] = byte((s4 >> 20) | (s5 << 1)) + s[14] = byte(s5 >> 7) + s[15] = byte((s5 >> 15) | (s6 << 6)) + s[16] = byte(s6 >> 2) + s[17] = byte(s6 >> 10) + s[18] = byte((s6 >> 18) | (s7 << 3)) + s[19] = byte(s7 >> 5) + s[20] = byte(s7 >> 13) + s[21] = byte(s8 >> 0) + s[22] = byte(s8 >> 8) + s[23] = byte((s8 >> 16) | (s9 << 5)) + s[24] = byte(s9 >> 3) + s[25] = byte(s9 >> 11) + s[26] = byte((s9 >> 19) | (s10 << 2)) + s[27] = byte(s10 >> 6) + s[28] = byte((s10 >> 14) | (s11 << 7)) + s[29] = byte(s11 >> 1) + s[30] = byte(s11 >> 9) + s[31] = byte(s11 >> 17) +} + +// Input: +// s[0]+256*s[1]+...+256^63*s[63] = s +// +// Output: +// s[0]+256*s[1]+...+256^31*s[31] = s mod l +// where l = 2^252 + 27742317777372353535851937790883648493. +func ScReduce(out *[32]byte, s *[64]byte) { + s0 := 2097151 & load3(s[:]) + s1 := 2097151 & (load4(s[2:]) >> 5) + s2 := 2097151 & (load3(s[5:]) >> 2) + s3 := 2097151 & (load4(s[7:]) >> 7) + s4 := 2097151 & (load4(s[10:]) >> 4) + s5 := 2097151 & (load3(s[13:]) >> 1) + s6 := 2097151 & (load4(s[15:]) >> 6) + s7 := 2097151 & (load3(s[18:]) >> 3) + s8 := 2097151 & load3(s[21:]) + s9 := 2097151 & (load4(s[23:]) >> 5) + s10 := 2097151 & (load3(s[26:]) >> 2) + s11 := 2097151 & (load4(s[28:]) >> 7) + s12 := 2097151 & (load4(s[31:]) >> 4) + s13 := 2097151 & (load3(s[34:]) >> 1) + s14 := 2097151 & (load4(s[36:]) >> 6) + s15 := 2097151 & (load3(s[39:]) >> 3) + s16 := 2097151 & load3(s[42:]) + s17 := 2097151 & (load4(s[44:]) >> 5) + s18 := 2097151 & (load3(s[47:]) >> 2) + s19 := 2097151 & (load4(s[49:]) >> 7) + s20 := 2097151 & (load4(s[52:]) >> 4) + s21 := 2097151 & (load3(s[55:]) >> 1) + s22 := 2097151 & (load4(s[57:]) >> 6) + s23 := (load4(s[60:]) >> 3) + + s11 += s23 * 666643 + s12 += s23 * 470296 + s13 += s23 * 654183 + s14 -= s23 * 997805 + s15 += s23 * 136657 + s16 -= s23 * 683901 + s23 = 0 + + s10 += s22 * 666643 + s11 += s22 * 470296 + s12 += s22 * 654183 + s13 -= s22 * 997805 + s14 += s22 * 136657 + s15 -= s22 * 683901 + s22 = 0 + + s9 += s21 * 666643 + s10 += s21 * 470296 + s11 += s21 * 654183 + s12 -= s21 * 997805 + s13 += s21 * 136657 + s14 -= s21 * 683901 + s21 = 0 + + s8 += s20 * 666643 + s9 += s20 * 470296 + s10 += s20 * 654183 + s11 -= s20 * 997805 + s12 += s20 * 136657 + s13 -= s20 * 683901 + s20 = 0 + + s7 += s19 * 666643 + s8 += s19 * 470296 + s9 += s19 * 654183 + s10 -= s19 * 997805 + s11 += s19 * 136657 + s12 -= s19 * 683901 + s19 = 0 + + s6 += s18 * 666643 + s7 += s18 * 470296 + s8 += s18 * 654183 + s9 -= s18 * 997805 + s10 += s18 * 136657 + s11 -= s18 * 683901 + s18 = 0 + + var carry [17]int64 + + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[12] = (s12 + (1 << 20)) >> 21 + s13 += carry[12] + s12 -= carry[12] << 21 + carry[14] = (s14 + (1 << 20)) >> 21 + s15 += carry[14] + s14 -= carry[14] << 21 + carry[16] = (s16 + (1 << 20)) >> 21 + s17 += carry[16] + s16 -= carry[16] << 21 + + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + carry[13] = (s13 + (1 << 20)) >> 21 + s14 += carry[13] + s13 -= carry[13] << 21 + carry[15] = (s15 + (1 << 20)) >> 21 + s16 += carry[15] + s15 -= carry[15] << 21 + + s5 += s17 * 666643 + s6 += s17 * 470296 + s7 += s17 * 654183 + s8 -= s17 * 997805 + s9 += s17 * 136657 + s10 -= s17 * 683901 + s17 = 0 + + s4 += s16 * 666643 + s5 += s16 * 470296 + s6 += s16 * 654183 + s7 -= s16 * 997805 + s8 += s16 * 136657 + s9 -= s16 * 683901 + s16 = 0 + + s3 += s15 * 666643 + s4 += s15 * 470296 + s5 += s15 * 654183 + s6 -= s15 * 997805 + s7 += s15 * 136657 + s8 -= s15 * 683901 + s15 = 0 + + s2 += s14 * 666643 + s3 += s14 * 470296 + s4 += s14 * 654183 + s5 -= s14 * 997805 + s6 += s14 * 136657 + s7 -= s14 * 683901 + s14 = 0 + + s1 += s13 * 666643 + s2 += s13 * 470296 + s3 += s13 * 654183 + s4 -= s13 * 997805 + s5 += s13 * 136657 + s6 -= s13 * 683901 + s13 = 0 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = (s0 + (1 << 20)) >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[2] = (s2 + (1 << 20)) >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[4] = (s4 + (1 << 20)) >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[6] = (s6 + (1 << 20)) >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[8] = (s8 + (1 << 20)) >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[10] = (s10 + (1 << 20)) >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + carry[1] = (s1 + (1 << 20)) >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[3] = (s3 + (1 << 20)) >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[5] = (s5 + (1 << 20)) >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[7] = (s7 + (1 << 20)) >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[9] = (s9 + (1 << 20)) >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[11] = (s11 + (1 << 20)) >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + carry[11] = s11 >> 21 + s12 += carry[11] + s11 -= carry[11] << 21 + + s0 += s12 * 666643 + s1 += s12 * 470296 + s2 += s12 * 654183 + s3 -= s12 * 997805 + s4 += s12 * 136657 + s5 -= s12 * 683901 + s12 = 0 + + carry[0] = s0 >> 21 + s1 += carry[0] + s0 -= carry[0] << 21 + carry[1] = s1 >> 21 + s2 += carry[1] + s1 -= carry[1] << 21 + carry[2] = s2 >> 21 + s3 += carry[2] + s2 -= carry[2] << 21 + carry[3] = s3 >> 21 + s4 += carry[3] + s3 -= carry[3] << 21 + carry[4] = s4 >> 21 + s5 += carry[4] + s4 -= carry[4] << 21 + carry[5] = s5 >> 21 + s6 += carry[5] + s5 -= carry[5] << 21 + carry[6] = s6 >> 21 + s7 += carry[6] + s6 -= carry[6] << 21 + carry[7] = s7 >> 21 + s8 += carry[7] + s7 -= carry[7] << 21 + carry[8] = s8 >> 21 + s9 += carry[8] + s8 -= carry[8] << 21 + carry[9] = s9 >> 21 + s10 += carry[9] + s9 -= carry[9] << 21 + carry[10] = s10 >> 21 + s11 += carry[10] + s10 -= carry[10] << 21 + + out[0] = byte(s0 >> 0) + out[1] = byte(s0 >> 8) + out[2] = byte((s0 >> 16) | (s1 << 5)) + out[3] = byte(s1 >> 3) + out[4] = byte(s1 >> 11) + out[5] = byte((s1 >> 19) | (s2 << 2)) + out[6] = byte(s2 >> 6) + out[7] = byte((s2 >> 14) | (s3 << 7)) + out[8] = byte(s3 >> 1) + out[9] = byte(s3 >> 9) + out[10] = byte((s3 >> 17) | (s4 << 4)) + out[11] = byte(s4 >> 4) + out[12] = byte(s4 >> 12) + out[13] = byte((s4 >> 20) | (s5 << 1)) + out[14] = byte(s5 >> 7) + out[15] = byte((s5 >> 15) | (s6 << 6)) + out[16] = byte(s6 >> 2) + out[17] = byte(s6 >> 10) + out[18] = byte((s6 >> 18) | (s7 << 3)) + out[19] = byte(s7 >> 5) + out[20] = byte(s7 >> 13) + out[21] = byte(s8 >> 0) + out[22] = byte(s8 >> 8) + out[23] = byte((s8 >> 16) | (s9 << 5)) + out[24] = byte(s9 >> 3) + out[25] = byte(s9 >> 11) + out[26] = byte((s9 >> 19) | (s10 << 2)) + out[27] = byte(s10 >> 6) + out[28] = byte((s10 >> 14) | (s11 << 7)) + out[29] = byte(s11 >> 1) + out[30] = byte(s11 >> 9) + out[31] = byte(s11 >> 17) +} diff --git a/src/vendor/github.com/davecgh/go-spew/LICENSE b/src/vendor/github.com/davecgh/go-spew/LICENSE new file mode 100644 index 000000000..c83641619 --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/vendor/github.com/davecgh/go-spew/spew/bypass.go b/src/vendor/github.com/davecgh/go-spew/spew/bypass.go new file mode 100644 index 000000000..8a4a6589a --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -0,0 +1,152 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build !js,!appengine,!safe,!disableunsafe + +package spew + +import ( + "reflect" + "unsafe" +) + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = false + + // ptrSize is the size of a pointer on the current arch. + ptrSize = unsafe.Sizeof((*byte)(nil)) +) + +var ( + // offsetPtr, offsetScalar, and offsetFlag are the offsets for the + // internal reflect.Value fields. These values are valid before golang + // commit ecccf07e7f9d which changed the format. The are also valid + // after commit 82f48826c6c7 which changed the format again to mirror + // the original format. Code in the init function updates these offsets + // as necessary. + offsetPtr = uintptr(ptrSize) + offsetScalar = uintptr(0) + offsetFlag = uintptr(ptrSize * 2) + + // flagKindWidth and flagKindShift indicate various bits that the + // reflect package uses internally to track kind information. + // + // flagRO indicates whether or not the value field of a reflect.Value is + // read-only. + // + // flagIndir indicates whether the value field of a reflect.Value is + // the actual data or a pointer to the data. + // + // These values are valid before golang commit 90a7c3c86944 which + // changed their positions. Code in the init function updates these + // flags as necessary. + flagKindWidth = uintptr(5) + flagKindShift = uintptr(flagKindWidth - 1) + flagRO = uintptr(1 << 0) + flagIndir = uintptr(1 << 1) +) + +func init() { + // Older versions of reflect.Value stored small integers directly in the + // ptr field (which is named val in the older versions). Versions + // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named + // scalar for this purpose which unfortunately came before the flag + // field, so the offset of the flag field is different for those + // versions. + // + // This code constructs a new reflect.Value from a known small integer + // and checks if the size of the reflect.Value struct indicates it has + // the scalar field. When it does, the offsets are updated accordingly. + vv := reflect.ValueOf(0xf00) + if unsafe.Sizeof(vv) == (ptrSize * 4) { + offsetScalar = ptrSize * 2 + offsetFlag = ptrSize * 3 + } + + // Commit 90a7c3c86944 changed the flag positions such that the low + // order bits are the kind. This code extracts the kind from the flags + // field and ensures it's the correct type. When it's not, the flag + // order has been changed to the newer format, so the flags are updated + // accordingly. + upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) + upfv := *(*uintptr)(upf) + flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { + flagKindShift = 0 + flagRO = 1 << 5 + flagIndir = 1 << 6 + + // Commit adf9b30e5594 modified the flags to separate the + // flagRO flag into two bits which specifies whether or not the + // field is embedded. This causes flagIndir to move over a bit + // and means that flagRO is the combination of either of the + // original flagRO bit and the new bit. + // + // This code detects the change by extracting what used to be + // the indirect bit to ensure it's set. When it's not, the flag + // order has been changed to the newer format, so the flags are + // updated accordingly. + if upfv&flagIndir == 0 { + flagRO = 3 << 5 + flagIndir = 1 << 7 + } + } +} + +// unsafeReflectValue converts the passed reflect.Value into a one that bypasses +// the typical safety restrictions preventing access to unaddressable and +// unexported data. It works by digging the raw pointer to the underlying +// value out of the protected value and generating a new unprotected (unsafe) +// reflect.Value to it. +// +// This allows us to check for implementations of the Stringer and error +// interfaces to be used for pretty printing ordinarily unaddressable and +// inaccessible values such as unexported struct fields. +func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { + indirects := 1 + vt := v.Type() + upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) + rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) + if rvf&flagIndir != 0 { + vt = reflect.PtrTo(v.Type()) + indirects++ + } else if offsetScalar != 0 { + // The value is in the scalar field when it's not one of the + // reference types. + switch vt.Kind() { + case reflect.Uintptr: + case reflect.Chan: + case reflect.Func: + case reflect.Map: + case reflect.Ptr: + case reflect.UnsafePointer: + default: + upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + + offsetScalar) + } + } + + pv := reflect.NewAt(vt, upv) + rv = pv + for i := 0; i < indirects; i++ { + rv = rv.Elem() + } + return rv +} diff --git a/src/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/src/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go new file mode 100644 index 000000000..1fe3cf3d5 --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build js appengine safe disableunsafe + +package spew + +import "reflect" + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = true +) + +// unsafeReflectValue typically converts the passed reflect.Value into a one +// that bypasses the typical safety restrictions preventing access to +// unaddressable and unexported data. However, doing this relies on access to +// the unsafe package. This is a stub version which simply returns the passed +// reflect.Value when the unsafe package is not available. +func unsafeReflectValue(v reflect.Value) reflect.Value { + return v +} diff --git a/src/vendor/github.com/davecgh/go-spew/spew/common.go b/src/vendor/github.com/davecgh/go-spew/spew/common.go new file mode 100644 index 000000000..7c519ff47 --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/common.go @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "reflect" + "sort" + "strconv" +) + +// Some constants in the form of bytes to avoid string overhead. This mirrors +// the technique used in the fmt package. +var ( + panicBytes = []byte("(PANIC=") + plusBytes = []byte("+") + iBytes = []byte("i") + trueBytes = []byte("true") + falseBytes = []byte("false") + interfaceBytes = []byte("(interface {})") + commaNewlineBytes = []byte(",\n") + newlineBytes = []byte("\n") + openBraceBytes = []byte("{") + openBraceNewlineBytes = []byte("{\n") + closeBraceBytes = []byte("}") + asteriskBytes = []byte("*") + colonBytes = []byte(":") + colonSpaceBytes = []byte(": ") + openParenBytes = []byte("(") + closeParenBytes = []byte(")") + spaceBytes = []byte(" ") + pointerChainBytes = []byte("->") + nilAngleBytes = []byte("") + maxNewlineBytes = []byte("\n") + maxShortBytes = []byte("") + circularBytes = []byte("") + circularShortBytes = []byte("") + invalidAngleBytes = []byte("") + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + percentBytes = []byte("%") + precisionBytes = []byte(".") + openAngleBytes = []byte("<") + closeAngleBytes = []byte(">") + openMapBytes = []byte("map[") + closeMapBytes = []byte("]") + lenEqualsBytes = []byte("len=") + capEqualsBytes = []byte("cap=") +) + +// hexDigits is used to map a decimal value to a hex digit. +var hexDigits = "0123456789abcdef" + +// catchPanic handles any panics that might occur during the handleMethods +// calls. +func catchPanic(w io.Writer, v reflect.Value) { + if err := recover(); err != nil { + w.Write(panicBytes) + fmt.Fprintf(w, "%v", err) + w.Write(closeParenBytes) + } +} + +// handleMethods attempts to call the Error and String methods on the underlying +// type the passed reflect.Value represents and outputes the result to Writer w. +// +// It handles panics in any called methods by catching and displaying the error +// as the formatted value. +func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { + // We need an interface to check if the type implements the error or + // Stringer interface. However, the reflect package won't give us an + // interface on certain things like unexported struct fields in order + // to enforce visibility rules. We use unsafe, when it's available, + // to bypass these restrictions since this package does not mutate the + // values. + if !v.CanInterface() { + if UnsafeDisabled { + return false + } + + v = unsafeReflectValue(v) + } + + // Choose whether or not to do error and Stringer interface lookups against + // the base type or a pointer to the base type depending on settings. + // Technically calling one of these methods with a pointer receiver can + // mutate the value, however, types which choose to satisify an error or + // Stringer interface with a pointer receiver should not be mutating their + // state inside these interface methods. + if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { + v = unsafeReflectValue(v) + } + if v.CanAddr() { + v = v.Addr() + } + + // Is it an error or Stringer? + switch iface := v.Interface().(type) { + case error: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.Error())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + + w.Write([]byte(iface.Error())) + return true + + case fmt.Stringer: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.String())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + w.Write([]byte(iface.String())) + return true + } + return false +} + +// printBool outputs a boolean value as true or false to Writer w. +func printBool(w io.Writer, val bool) { + if val { + w.Write(trueBytes) + } else { + w.Write(falseBytes) + } +} + +// printInt outputs a signed integer value to Writer w. +func printInt(w io.Writer, val int64, base int) { + w.Write([]byte(strconv.FormatInt(val, base))) +} + +// printUint outputs an unsigned integer value to Writer w. +func printUint(w io.Writer, val uint64, base int) { + w.Write([]byte(strconv.FormatUint(val, base))) +} + +// printFloat outputs a floating point value using the specified precision, +// which is expected to be 32 or 64bit, to Writer w. +func printFloat(w io.Writer, val float64, precision int) { + w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) +} + +// printComplex outputs a complex value using the specified float precision +// for the real and imaginary parts to Writer w. +func printComplex(w io.Writer, c complex128, floatPrecision int) { + r := real(c) + w.Write(openParenBytes) + w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) + i := imag(c) + if i >= 0 { + w.Write(plusBytes) + } + w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + w.Write(iBytes) + w.Write(closeParenBytes) +} + +// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x' +// prefix to Writer w. +func printHexPtr(w io.Writer, p uintptr) { + // Null pointer. + num := uint64(p) + if num == 0 { + w.Write(nilAngleBytes) + return + } + + // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix + buf := make([]byte, 18) + + // It's simpler to construct the hex string right to left. + base := uint64(16) + i := len(buf) - 1 + for num >= base { + buf[i] = hexDigits[num%base] + num /= base + i-- + } + buf[i] = hexDigits[num] + + // Add '0x' prefix. + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + + // Strip unused leading bytes. + buf = buf[i:] + w.Write(buf) +} + +// valuesSorter implements sort.Interface to allow a slice of reflect.Value +// elements to be sorted. +type valuesSorter struct { + values []reflect.Value + strings []string // either nil or same len and values + cs *ConfigState +} + +// newValuesSorter initializes a valuesSorter instance, which holds a set of +// surrogate keys on which the data should be sorted. It uses flags in +// ConfigState to decide if and how to populate those surrogate keys. +func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { + vs := &valuesSorter{values: values, cs: cs} + if canSortSimply(vs.values[0].Kind()) { + return vs + } + if !cs.DisableMethods { + vs.strings = make([]string, len(values)) + for i := range vs.values { + b := bytes.Buffer{} + if !handleMethods(cs, &b, vs.values[i]) { + vs.strings = nil + break + } + vs.strings[i] = b.String() + } + } + if vs.strings == nil && cs.SpewKeys { + vs.strings = make([]string, len(values)) + for i := range vs.values { + vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) + } + } + return vs +} + +// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted +// directly, or whether it should be considered for sorting by surrogate keys +// (if the ConfigState allows it). +func canSortSimply(kind reflect.Kind) bool { + // This switch parallels valueSortLess, except for the default case. + switch kind { + case reflect.Bool: + return true + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Uintptr: + return true + case reflect.Array: + return true + } + return false +} + +// Len returns the number of values in the slice. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Len() int { + return len(s.values) +} + +// Swap swaps the values at the passed indices. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + if s.strings != nil { + s.strings[i], s.strings[j] = s.strings[j], s.strings[i] + } +} + +// valueSortLess returns whether the first value should sort before the second +// value. It is used by valueSorter.Less as part of the sort.Interface +// implementation. +func valueSortLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Bool: + return !a.Bool() && b.Bool() + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return a.Int() < b.Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return a.Uint() < b.Uint() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.String: + return a.String() < b.String() + case reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Array: + // Compare the contents of both arrays. + l := a.Len() + for i := 0; i < l; i++ { + av := a.Index(i) + bv := b.Index(i) + if av.Interface() == bv.Interface() { + continue + } + return valueSortLess(av, bv) + } + } + return a.String() < b.String() +} + +// Less returns whether the value at index i should sort before the +// value at index j. It is part of the sort.Interface implementation. +func (s *valuesSorter) Less(i, j int) bool { + if s.strings == nil { + return valueSortLess(s.values[i], s.values[j]) + } + return s.strings[i] < s.strings[j] +} + +// sortValues is a sort function that handles both native types and any type that +// can be converted to error or Stringer. Other inputs are sorted according to +// their Value.String() value to ensure display stability. +func sortValues(values []reflect.Value, cs *ConfigState) { + if len(values) == 0 { + return + } + sort.Sort(newValuesSorter(values, cs)) +} diff --git a/src/vendor/github.com/davecgh/go-spew/spew/config.go b/src/vendor/github.com/davecgh/go-spew/spew/config.go new file mode 100644 index 000000000..2e3d22f31 --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/config.go @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "os" +) + +// ConfigState houses the configuration options used by spew to format and +// display values. There is a global instance, Config, that is used to control +// all top-level Formatter and Dump functionality. Each ConfigState instance +// provides methods equivalent to the top-level functions. +// +// The zero value for ConfigState provides no indentation. You would typically +// want to set it to a space or a tab. +// +// Alternatively, you can use NewDefaultConfig to get a ConfigState instance +// with default settings. See the documentation of NewDefaultConfig for default +// values. +type ConfigState struct { + // Indent specifies the string to use for each indentation level. The + // global config instance that all top-level functions use set this to a + // single space by default. If you would like more indentation, you might + // set this to a tab with "\t" or perhaps two spaces with " ". + Indent string + + // MaxDepth controls the maximum number of levels to descend into nested + // data structures. The default, 0, means there is no limit. + // + // NOTE: Circular data structures are properly detected, so it is not + // necessary to set this value unless you specifically want to limit deeply + // nested data structures. + MaxDepth int + + // DisableMethods specifies whether or not error and Stringer interfaces are + // invoked for types that implement them. + DisableMethods bool + + // DisablePointerMethods specifies whether or not to check for and invoke + // error and Stringer interfaces on types which only accept a pointer + // receiver when the current type is not a pointer. + // + // NOTE: This might be an unsafe action since calling one of these methods + // with a pointer receiver could technically mutate the value, however, + // in practice, types which choose to satisify an error or Stringer + // interface with a pointer receiver should not be mutating their state + // inside these interface methods. As a result, this option relies on + // access to the unsafe package, so it will not have any effect when + // running in environments without access to the unsafe package such as + // Google App Engine or with the "safe" build tag specified. + DisablePointerMethods bool + + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + + // ContinueOnMethod specifies whether or not recursion should continue once + // a custom error or Stringer interface is invoked. The default, false, + // means it will print the results of invoking the custom error or Stringer + // interface and return immediately instead of continuing to recurse into + // the internals of the data type. + // + // NOTE: This flag does not have any effect if method invocation is disabled + // via the DisableMethods or DisablePointerMethods options. + ContinueOnMethod bool + + // SortKeys specifies map keys should be sorted before being printed. Use + // this to have a more deterministic, diffable output. Note that only + // native types (bool, int, uint, floats, uintptr and string) and types + // that support the error or Stringer interfaces (if methods are + // enabled) are supported, with other types sorted according to the + // reflect.Value.String() output which guarantees display stability. + SortKeys bool + + // SpewKeys specifies that, as a last resort attempt, map keys should + // be spewed to strings and sorted by those strings. This is only + // considered if SortKeys is true. + SpewKeys bool +} + +// Config is the active configuration of the top-level functions. +// The configuration can be changed by modifying the contents of spew.Config. +var Config = ConfigState{Indent: " "} + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the formatted string as a value that satisfies error. See NewFormatter +// for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, c.convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, c.convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, c.convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a Formatter interface returned by c.NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, c.convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Print(a ...interface{}) (n int, err error) { + return fmt.Print(c.convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, c.convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Println(a ...interface{}) (n int, err error) { + return fmt.Println(c.convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprint(a ...interface{}) string { + return fmt.Sprint(c.convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, c.convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a Formatter interface returned by c.NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintln(a ...interface{}) string { + return fmt.Sprintln(c.convertArgs(a)...) +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +c.Printf, c.Println, or c.Printf. +*/ +func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(c, v) +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { + fdump(c, w, a...) +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by modifying the public members +of c. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func (c *ConfigState) Dump(a ...interface{}) { + fdump(c, os.Stdout, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func (c *ConfigState) Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(c, &buf, a...) + return buf.String() +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a spew Formatter interface using +// the ConfigState associated with s. +func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = newFormatter(c, arg) + } + return formatters +} + +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} diff --git a/src/vendor/github.com/davecgh/go-spew/spew/doc.go b/src/vendor/github.com/davecgh/go-spew/spew/doc.go new file mode 100644 index 000000000..aacaac6f1 --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/doc.go @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package spew implements a deep pretty printer for Go data structures to aid in +debugging. + +A quick overview of the additional features spew provides over the built-in +printing facilities for Go data types are as follows: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) + +There are two different approaches spew allows for dumping Go data structures: + + * Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + * A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt + +Quick Start + +This section demonstrates how to quickly get started with spew. See the +sections below for further details on formatting and configuration options. + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + spew.Dump(myVar1, myVar2, ...) + spew.Fdump(someWriter, myVar1, myVar2, ...) + str := spew.Sdump(myVar1, myVar2, ...) + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with +%v (most compact), %+v (adds pointer addresses), %#v (adds types), or +%#+v (adds types and pointer addresses): + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available +via the spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +The following configuration options are available: + * Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + * MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + * DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + * DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + * DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + * DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + * ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + * SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + * SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +Dump Usage + +Simply call spew.Dump with a list of variables you want to dump: + + spew.Dump(myVar1, myVar2, ...) + +You may also call spew.Fdump if you would prefer to output to an arbitrary +io.Writer. For example, to dump to standard error: + + spew.Fdump(os.Stderr, myVar1, myVar2, ...) + +A third option is to call spew.Sdump to get the formatted output as a string: + + str := spew.Sdump(myVar1, myVar2, ...) + +Sample Dump Output + +See the Dump example for details on the setup of the types and variables being +shown here. + + (main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) + }), + ExportedField: (map[interface {}]interface {}) (len=1) { + (string) (len=3) "one": (bool) true + } + } + +Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C +command as shown. + ([]uint8) (len=32 cap=32) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| + } + +Custom Formatter + +Spew provides a custom formatter that implements the fmt.Formatter interface +so that it integrates cleanly with standard fmt package printing functions. The +formatter is useful for inline printing of smaller data types similar to the +standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Custom Formatter Usage + +The simplest way to make use of the spew custom formatter is to call one of the +convenience functions such as spew.Printf, spew.Println, or spew.Printf. The +functions have syntax you are most likely already familiar with: + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Println(myVar, myVar2) + spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +See the Index for the full list convenience functions. + +Sample Formatter Output + +Double pointer to a uint8: + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 + +Pointer to circular struct with a uint8 field and a pointer to itself: + %v: <*>{1 <*>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} + +See the Printf example for details on the setup of variables being shown +here. + +Errors + +Since it is possible for custom Stringer/error interfaces to panic, spew +detects them and handles them internally by printing the panic information +inline with the output. Since spew is intended to provide deep pretty printing +capabilities on structures, it intentionally does not return any errors. +*/ +package spew diff --git a/src/vendor/github.com/davecgh/go-spew/spew/dump.go b/src/vendor/github.com/davecgh/go-spew/spew/dump.go new file mode 100644 index 000000000..df1d582a7 --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // uint8Type is a reflect.Type representing a uint8. It is used to + // convert cgo types to uint8 slices for hexdumping. + uint8Type = reflect.TypeOf(uint8(0)) + + // cCharRE is a regular expression that matches a cgo char. + // It is used to detect character arrays to hexdump them. + cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") + + // cUnsignedCharRE is a regular expression that matches a cgo unsigned + // char. It is used to detect unsigned character arrays to hexdump + // them. + cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") + + // cUint8tCharRE is a regular expression that matches a cgo uint8_t. + // It is used to detect uint8_t arrays to hexdump them. + cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { + w io.Writer + depth int + pointers map[uintptr]int + ignoreNextType bool + ignoreNextIndent bool + cs *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { + if d.ignoreNextIndent { + d.ignoreNextIndent = false + return + } + d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range d.pointers { + if depth >= d.depth { + delete(d.pointers, k) + } + } + + // Keep list of all dereferenced pointers to show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by dereferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := d.pointers[addr]; ok && pd < d.depth { + cycleFound = true + indirects-- + break + } + d.pointers[addr] = d.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type information. + d.w.Write(openParenBytes) + d.w.Write(bytes.Repeat(asteriskBytes, indirects)) + d.w.Write([]byte(ve.Type().String())) + d.w.Write(closeParenBytes) + + // Display pointer information. + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { + d.w.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + d.w.Write(pointerChainBytes) + } + printHexPtr(d.w, addr) + } + d.w.Write(closeParenBytes) + } + + // Display dereferenced value. + d.w.Write(openParenBytes) + switch { + case nilFound == true: + d.w.Write(nilAngleBytes) + + case cycleFound == true: + d.w.Write(circularBytes) + + default: + d.ignoreNextType = true + d.dump(ve) + } + d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices. Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { + // Determine whether this type should be hex dumped or not. Also, + // for types which should be hexdumped, try to use the underlying data + // first, then fall back to trying to convert them to a uint8 slice. + var buf []uint8 + doConvert := false + doHexDump := false + numEntries := v.Len() + if numEntries > 0 { + vt := v.Index(0).Type() + vts := vt.String() + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts): + fallthrough + case cUnsignedCharRE.MatchString(vts): + fallthrough + case cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + // We need an addressable interface to convert the type + // to a byte slice. However, the reflect package won't + // give us an interface on certain things like + // unexported struct fields in order to enforce + // visibility rules. We use unsafe, when available, to + // bypass these restrictions since this package does not + // mutate the values. + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if !UnsafeDisabled { + vs = vs.Slice(0, numEntries) + + // Use the existing uint8 slice if it can be + // type asserted. + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + buf = slice + doHexDump = true + break + } + } + + // The underlying data needs to be converted if it can't + // be type asserted to a uint8 slice. + doConvert = true + } + + // Copy and convert the underlying type if needed. + if doConvert && vt.ConvertibleTo(uint8Type) { + // Convert and copy each element into a uint8 byte + // slice. + buf = make([]uint8, numEntries) + for i := 0; i < numEntries; i++ { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) + } + doHexDump = true + } + } + + // Hexdump the entire slice as needed. + if doHexDump { + indent := strings.Repeat(d.cs.Indent, d.depth) + str := indent + hex.Dump(buf) + str = strings.Replace(str, "\n", "\n"+indent, -1) + str = strings.TrimRight(str, d.cs.Indent) + d.w.Write([]byte(str)) + return + } + + // Recursively call dump for each item. + for i := 0; i < numEntries; i++ { + d.dump(d.unpackValue(v.Index(i))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } +} + +// dump is the main workhorse for dumping a value. It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately. It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + d.w.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + d.indent() + d.dumpPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !d.ignoreNextType { + d.indent() + d.w.Write(openParenBytes) + d.w.Write([]byte(v.Type().String())) + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + d.ignoreNextType = false + + // Display length and capacity if the built-in len and cap functions + // work with the value's kind and the len/cap itself is non-zero. + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + } + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { + d.w.Write(openParenBytes) + if valueLen != 0 { + d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), 10) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + d.w.Write(spaceBytes) + } + d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), 10) + } + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + + // Call Stringer/error interfaces if they exist and the handle methods flag + // is enabled + if !d.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(d.cs, d.w, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(d.w, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(d.w, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(d.w, v.Uint(), 10) + + case reflect.Float32: + printFloat(d.w, v.Float(), 32) + + case reflect.Float64: + printFloat(d.w, v.Float(), 64) + + case reflect.Complex64: + printComplex(d.w, v.Complex(), 32) + + case reflect.Complex128: + printComplex(d.w, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.String: + d.w.Write([]byte(strconv.Quote(v.String()))) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + d.w.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + numEntries := v.Len() + keys := v.MapKeys() + if d.cs.SortKeys { + sortValues(keys, d.cs) + } + for i, key := range keys { + d.dump(d.unpackValue(key)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.MapIndex(key))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Struct: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := 0; i < numFields; i++ { + d.indent() + vtf := vt.Field(i) + d.w.Write([]byte(vtf.Name)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(d.w, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(d.w, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it in case any new + // types are added. + default: + if v.CanInterface() { + fmt.Fprintf(d.w, "%v", v.Interface()) + } else { + fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { + for _, arg := range a { + if arg == nil { + w.Write(interfaceBytes) + w.Write(spaceBytes) + w.Write(nilAngleBytes) + w.Write(newlineBytes) + continue + } + + d := dumpState{w: w, cs: cs} + d.pointers = make(map[uintptr]int) + d.dump(reflect.ValueOf(arg)) + d.w.Write(newlineBytes) + } +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { + fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(&Config, &buf, a...) + return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { + fdump(&Config, os.Stdout, a...) +} diff --git a/src/vendor/github.com/davecgh/go-spew/spew/format.go b/src/vendor/github.com/davecgh/go-spew/spew/format.go new file mode 100644 index 000000000..c49875bac --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/format.go @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// supportedFlags is a list of all the character flags supported by fmt package. +const supportedFlags = "0-+# " + +// formatState implements the fmt.Formatter interface and contains information +// about the state of a formatting operation. The NewFormatter function can +// be used to get a new Formatter which can be used directly as arguments +// in standard fmt package printing calls. +type formatState struct { + value interface{} + fs fmt.State + depth int + pointers map[uintptr]int + ignoreNextType bool + cs *ConfigState +} + +// buildDefaultFormat recreates the original format string without precision +// and width information to pass in to fmt.Sprintf in the case of an +// unrecognized type. Unless new types are added to the language, this +// function won't ever be called. +func (f *formatState) buildDefaultFormat() (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + buf.WriteRune('v') + + format = buf.String() + return format +} + +// constructOrigFormat recreates the original format string including precision +// and width information to pass along to the standard fmt package. This allows +// automatic deferral of all format strings this package doesn't support. +func (f *formatState) constructOrigFormat(verb rune) (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + if width, ok := f.fs.Width(); ok { + buf.WriteString(strconv.Itoa(width)) + } + + if precision, ok := f.fs.Precision(); ok { + buf.Write(precisionBytes) + buf.WriteString(strconv.Itoa(precision)) + } + + buf.WriteRune(verb) + + format = buf.String() + return format +} + +// unpackValue returns values inside of non-nil interfaces when possible and +// ensures that types for values which have been unpacked from an interface +// are displayed when the show types flag is also set. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (f *formatState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + f.ignoreNextType = false + if !v.IsNil() { + v = v.Elem() + } + } + return v +} + +// formatPtr handles formatting of pointers by indirecting them as necessary. +func (f *formatState) formatPtr(v reflect.Value) { + // Display nil if top level pointer is nil. + showTypes := f.fs.Flag('#') + if v.IsNil() && (!showTypes || f.ignoreNextType) { + f.fs.Write(nilAngleBytes) + return + } + + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range f.pointers { + if depth >= f.depth { + delete(f.pointers, k) + } + } + + // Keep list of all dereferenced pointers to possibly show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by derferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := f.pointers[addr]; ok && pd < f.depth { + cycleFound = true + indirects-- + break + } + f.pointers[addr] = f.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type or indirection level depending on flags. + if showTypes && !f.ignoreNextType { + f.fs.Write(openParenBytes) + f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) + f.fs.Write([]byte(ve.Type().String())) + f.fs.Write(closeParenBytes) + } else { + if nilFound || cycleFound { + indirects += strings.Count(ve.Type().String(), "*") + } + f.fs.Write(openAngleBytes) + f.fs.Write([]byte(strings.Repeat("*", indirects))) + f.fs.Write(closeAngleBytes) + } + + // Display pointer information depending on flags. + if f.fs.Flag('+') && (len(pointerChain) > 0) { + f.fs.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + f.fs.Write(pointerChainBytes) + } + printHexPtr(f.fs, addr) + } + f.fs.Write(closeParenBytes) + } + + // Display dereferenced value. + switch { + case nilFound == true: + f.fs.Write(nilAngleBytes) + + case cycleFound == true: + f.fs.Write(circularShortBytes) + + default: + f.ignoreNextType = true + f.format(ve) + } +} + +// format is the main workhorse for providing the Formatter interface. It +// uses the passed reflect value to figure out what kind of object we are +// dealing with and formats it appropriately. It is a recursive function, +// however circular data structures are detected and handled properly. +func (f *formatState) format(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + f.fs.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + f.formatPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !f.ignoreNextType && f.fs.Flag('#') { + f.fs.Write(openParenBytes) + f.fs.Write([]byte(v.Type().String())) + f.fs.Write(closeParenBytes) + } + f.ignoreNextType = false + + // Call Stringer/error interfaces if they exist and the handle methods + // flag is enabled. + if !f.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(f.cs, f.fs, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(f.fs, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(f.fs, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(f.fs, v.Uint(), 10) + + case reflect.Float32: + printFloat(f.fs, v.Float(), 32) + + case reflect.Float64: + printFloat(f.fs, v.Float(), 64) + + case reflect.Complex64: + printComplex(f.fs, v.Complex(), 32) + + case reflect.Complex128: + printComplex(f.fs, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := 0; i < numEntries; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + f.fs.Write(closeBracketBytes) + + case reflect.String: + f.fs.Write([]byte(v.String())) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + f.fs.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + + f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) + } + } + f.depth-- + f.fs.Write(closeMapBytes) + + case reflect.Struct: + numFields := v.NumField() + f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := 0; i < numFields; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + f.fs.Write([]byte(vtf.Name)) + f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + f.fs.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(f.fs, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(f.fs, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it if any get added. + default: + format := f.buildDefaultFormat() + if v.CanInterface() { + fmt.Fprintf(f.fs, format, v.Interface()) + } else { + fmt.Fprintf(f.fs, format, v.String()) + } + } +} + +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + fs.Write(interfaceBytes) + } + fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + +// newFormatter is a helper function to consolidate the logic from the various +// public methods which take varying config states. +func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { + fs := &formatState{value: v, cs: cs} + fs.pointers = make(map[uintptr]int) + return fs +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +Printf, Println, or Fprintf. +*/ +func NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(&Config, v) +} diff --git a/src/vendor/github.com/davecgh/go-spew/spew/spew.go b/src/vendor/github.com/davecgh/go-spew/spew/spew.go new file mode 100644 index 000000000..32c0e3388 --- /dev/null +++ b/src/vendor/github.com/davecgh/go-spew/spew/spew.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "fmt" + "io" +) + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the formatted string as a value that satisfies error. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a default Formatter interface returned by NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) +func Print(a ...interface{}) (n int, err error) { + return fmt.Print(convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) +func Println(a ...interface{}) (n int, err error) { + return fmt.Println(convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprint(a ...interface{}) string { + return fmt.Sprint(convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintln(a ...interface{}) string { + return fmt.Sprintln(convertArgs(a)...) +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a default spew Formatter interface. +func convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = NewFormatter(arg) + } + return formatters +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/LICENSE b/src/vendor/github.com/dgrijalva/jwt-go/LICENSE new file mode 100644 index 000000000..df83a9c2f --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/LICENSE @@ -0,0 +1,8 @@ +Copyright (c) 2012 Dave Grijalva + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/src/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md b/src/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md new file mode 100644 index 000000000..fd62e9490 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/MIGRATION_GUIDE.md @@ -0,0 +1,96 @@ +## Migration Guide from v2 -> v3 + +Version 3 adds several new, frequently requested features. To do so, it introduces a few breaking changes. We've worked to keep these as minimal as possible. This guide explains the breaking changes and how you can quickly update your code. + +### `Token.Claims` is now an interface type + +The most requested feature from the 2.0 verison of this library was the ability to provide a custom type to the JSON parser for claims. This was implemented by introducing a new interface, `Claims`, to replace `map[string]interface{}`. We also included two concrete implementations of `Claims`: `MapClaims` and `StandardClaims`. + +`MapClaims` is an alias for `map[string]interface{}` with built in validation behavior. It is the default claims type when using `Parse`. The usage is unchanged except you must type cast the claims property. + +The old example for parsing a token looked like this.. + +```go + if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { + fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) + } +``` + +is now directly mapped to... + +```go + if token, err := jwt.Parse(tokenString, keyLookupFunc); err == nil { + claims := token.Claims.(jwt.MapClaims) + fmt.Printf("Token for user %v expires %v", claims["user"], claims["exp"]) + } +``` + +`StandardClaims` is designed to be embedded in your custom type. You can supply a custom claims type with the new `ParseWithClaims` function. Here's an example of using a custom claims type. + +```go + type MyCustomClaims struct { + User string + *StandardClaims + } + + if token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, keyLookupFunc); err == nil { + claims := token.Claims.(*MyCustomClaims) + fmt.Printf("Token for user %v expires %v", claims.User, claims.StandardClaims.ExpiresAt) + } +``` + +### `ParseFromRequest` has been moved + +To keep this library focused on the tokens without becoming overburdened with complex request processing logic, `ParseFromRequest` and its new companion `ParseFromRequestWithClaims` have been moved to a subpackage, `request`. The method signatues have also been augmented to receive a new argument: `Extractor`. + +`Extractors` do the work of picking the token string out of a request. The interface is simple and composable. + +This simple parsing example: + +```go + if token, err := jwt.ParseFromRequest(tokenString, req, keyLookupFunc); err == nil { + fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) + } +``` + +is directly mapped to: + +```go + if token, err := request.ParseFromRequest(tokenString, request.OAuth2Extractor, req, keyLookupFunc); err == nil { + fmt.Printf("Token for user %v expires %v", token.Claims["user"], token.Claims["exp"]) + } +``` + +There are several concrete `Extractor` types provided for your convenience: + +* `HeaderExtractor` will search a list of headers until one contains content. +* `ArgumentExtractor` will search a list of keys in request query and form arguments until one contains content. +* `MultiExtractor` will try a list of `Extractors` in order until one returns content. +* `AuthorizationHeaderExtractor` will look in the `Authorization` header for a `Bearer` token. +* `OAuth2Extractor` searches the places an OAuth2 token would be specified (per the spec): `Authorization` header and `access_token` argument +* `PostExtractionFilter` wraps an `Extractor`, allowing you to process the content before it's parsed. A simple example is stripping the `Bearer ` text from a header + + +### RSA signing methods no longer accept `[]byte` keys + +Due to a [critical vulnerability](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/), we've decided the convenience of accepting `[]byte` instead of `rsa.PublicKey` or `rsa.PrivateKey` isn't worth the risk of misuse. + +To replace this behavior, we've added two helper methods: `ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error)` and `ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error)`. These are just simple helpers for unpacking PEM encoded PKCS1 and PKCS8 keys. If your keys are encoded any other way, all you need to do is convert them to the `crypto/rsa` package's types. + +```go + func keyLookupFunc(*Token) (interface{}, error) { + // Don't forget to validate the alg is what you expect: + if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { + return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) + } + + // Look up key + key, err := lookupPublicKey(token.Header["kid"]) + if err != nil { + return nil, err + } + + // Unpack key from PEM encoded PKCS8 + return jwt.ParseRSAPublicKeyFromPEM(key) + } +``` diff --git a/src/vendor/github.com/dgrijalva/jwt-go/README.md b/src/vendor/github.com/dgrijalva/jwt-go/README.md new file mode 100644 index 000000000..00f613672 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/README.md @@ -0,0 +1,85 @@ +A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html) + +[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](https://travis-ci.org/dgrijalva/jwt-go) + +**BREAKING CHANGES:*** Version 3.0.0 is here. It includes _a lot_ of changes including a few that break the API. We've tried to break as few things as possible, so there should just be a few type signature changes. A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. + +**NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected. There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API. In the short term, please make sure your implementation verifies the `alg` is what you expect. + + +## What the heck is a JWT? + +JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens. + +In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way. + +The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. + +The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-jones-json-web-token.html) for information about reserved keys and the proper way to add your own. + +## What's in the box? + +This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. + +## Examples + +See [the project documentation](https://godoc.org/github.com/dgrijalva/jwt-go) for examples of usage: + +* [Simple example of parsing and validating a token](https://godoc.org/github.com/dgrijalva/jwt-go#example_Parse_hmac) +* [Simple example of building and signing a token](https://godoc.org/github.com/dgrijalva/jwt-go#example_New_hmac) +* [Directory of Examples](https://godoc.org/github.com/dgrijalva/jwt-go#pkg-examples) + +## Extensions + +This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. + +Here's an example of an extension that integrates with the Google App Engine signing tools: https://github.com/someone1/gcp-jwt-go + +## Compliance + +This library was last reviewed to comply with [RTF 7519](http://www.rfc-editor.org/info/rfc7519) dated May 2015 with a few notable differences: + +* In order to protect against accidental use of [Unsecured JWTs](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#UnsecuredJWT), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key. + +## Project Status & Versioning + +This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). + +This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases). + +While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users. You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`. It will do the right thing WRT semantic versioning. + +## Usage Tips + +### Signing vs Encryption + +A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data: + +* The author of the token was in the possession of the signing secret +* The data has not been modified since it was signed + +It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. + +### Choosing a Signing Method + +There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric. + +Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation. + +Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. + +### JWT and OAuth + +It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. + +Without going too far down the rabbit hole, here's a description of the interaction of these technologies: + +* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth. +* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. +* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL. + +## More + +Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go). + +The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in to documentation. diff --git a/src/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md b/src/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md new file mode 100644 index 000000000..b605b4509 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md @@ -0,0 +1,105 @@ +## `jwt-go` Version History + +#### 3.0.0 + +* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code + * Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods. + * `ParseFromRequest` has been moved to `request` subpackage and usage has changed + * The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims. +* Other Additions and Changes + * Added `Claims` interface type to allow users to decode the claims into a custom type + * Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into. + * Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage + * Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims` + * Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`. + * Added several new, more specific, validation errors to error type bitmask + * Moved examples from README to executable example files + * Signing method registry is now thread safe + * Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser) + +#### 2.7.0 + +This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes. + +* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying +* Error text for expired tokens includes how long it's been expired +* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM` +* Documentation updates + +#### 2.6.0 + +* Exposed inner error within ValidationError +* Fixed validation errors when using UseJSONNumber flag +* Added several unit tests + +#### 2.5.0 + +* Added support for signing method none. You shouldn't use this. The API tries to make this clear. +* Updated/fixed some documentation +* Added more helpful error message when trying to parse tokens that begin with `BEARER ` + +#### 2.4.0 + +* Added new type, Parser, to allow for configuration of various parsing parameters + * You can now specify a list of valid signing methods. Anything outside this set will be rejected. + * You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON +* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go) +* Fixed some bugs with ECDSA parsing + +#### 2.3.0 + +* Added support for ECDSA signing methods +* Added support for RSA PSS signing methods (requires go v1.4) + +#### 2.2.0 + +* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic. + +#### 2.1.0 + +Backwards compatible API change that was missed in 2.0.0. + +* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte` + +#### 2.0.0 + +There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. + +The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. + +It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. + +* **Compatibility Breaking Changes** + * `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct` + * `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct` + * `KeyFunc` now returns `interface{}` instead of `[]byte` + * `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key + * `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key +* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type. + * Added public package global `SigningMethodHS256` + * Added public package global `SigningMethodHS384` + * Added public package global `SigningMethodHS512` +* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type. + * Added public package global `SigningMethodRS256` + * Added public package global `SigningMethodRS384` + * Added public package global `SigningMethodRS512` +* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged. +* Refactored the RSA implementation to be easier to read +* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM` + +#### 1.0.2 + +* Fixed bug in parsing public keys from certificates +* Added more tests around the parsing of keys for RS256 +* Code refactoring in RS256 implementation. No functional changes + +#### 1.0.1 + +* Fixed panic if RS256 signing method was passed an invalid key + +#### 1.0.0 + +* First versioned release +* API stabilized +* Supports creating, signing, parsing, and validating JWT tokens +* Supports RS256 and HS256 signing methods \ No newline at end of file diff --git a/src/vendor/github.com/dgrijalva/jwt-go/claims.go b/src/vendor/github.com/dgrijalva/jwt-go/claims.go new file mode 100644 index 000000000..f0228f02e --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/claims.go @@ -0,0 +1,134 @@ +package jwt + +import ( + "crypto/subtle" + "fmt" + "time" +) + +// For a type to be a Claims object, it must just have a Valid method that determines +// if the token is invalid for any supported reason +type Claims interface { + Valid() error +} + +// Structured version of Claims Section, as referenced at +// https://tools.ietf.org/html/rfc7519#section-4.1 +// See examples for how to use this with your own claim types +type StandardClaims struct { + Audience string `json:"aud,omitempty"` + ExpiresAt int64 `json:"exp,omitempty"` + Id string `json:"jti,omitempty"` + IssuedAt int64 `json:"iat,omitempty"` + Issuer string `json:"iss,omitempty"` + NotBefore int64 `json:"nbf,omitempty"` + Subject string `json:"sub,omitempty"` +} + +// Validates time based claims "exp, iat, nbf". +// There is no accounting for clock skew. +// As well, if any of the above claims are not in the token, it will still +// be considered a valid claim. +func (c StandardClaims) Valid() error { + vErr := new(ValidationError) + now := TimeFunc().Unix() + + // The claims below are optional, by default, so if they are set to the + // default value in Go, let's not fail the verification for them. + if c.VerifyExpiresAt(now, false) == false { + delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) + vErr.Inner = fmt.Errorf("token is expired by %v", delta) + vErr.Errors |= ValidationErrorExpired + } + + if c.VerifyIssuedAt(now, false) == false { + vErr.Inner = fmt.Errorf("Token used before issued") + vErr.Errors |= ValidationErrorIssuedAt + } + + if c.VerifyNotBefore(now, false) == false { + vErr.Inner = fmt.Errorf("token is not valid yet") + vErr.Errors |= ValidationErrorNotValidYet + } + + if vErr.valid() { + return nil + } + + return vErr +} + +// Compares the aud claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { + return verifyAud(c.Audience, cmp, req) +} + +// Compares the exp claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { + return verifyExp(c.ExpiresAt, cmp, req) +} + +// Compares the iat claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { + return verifyIat(c.IssuedAt, cmp, req) +} + +// Compares the iss claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { + return verifyIss(c.Issuer, cmp, req) +} + +// Compares the nbf claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { + return verifyNbf(c.NotBefore, cmp, req) +} + +// ----- helpers + +func verifyAud(aud string, cmp string, required bool) bool { + if aud == "" { + return !required + } + if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 { + return true + } else { + return false + } +} + +func verifyExp(exp int64, now int64, required bool) bool { + if exp == 0 { + return !required + } + return now <= exp +} + +func verifyIat(iat int64, now int64, required bool) bool { + if iat == 0 { + return !required + } + return now >= iat +} + +func verifyIss(iss string, cmp string, required bool) bool { + if iss == "" { + return !required + } + if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { + return true + } else { + return false + } +} + +func verifyNbf(nbf int64, now int64, required bool) bool { + if nbf == 0 { + return !required + } + return now >= nbf +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/doc.go b/src/vendor/github.com/dgrijalva/jwt-go/doc.go new file mode 100644 index 000000000..a86dc1a3b --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/doc.go @@ -0,0 +1,4 @@ +// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html +// +// See README.md for more info. +package jwt diff --git a/src/vendor/github.com/dgrijalva/jwt-go/ecdsa.go b/src/vendor/github.com/dgrijalva/jwt-go/ecdsa.go new file mode 100644 index 000000000..2f59a2223 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/ecdsa.go @@ -0,0 +1,147 @@ +package jwt + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "errors" + "math/big" +) + +var ( + // Sadly this is missing from crypto/ecdsa compared to crypto/rsa + ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") +) + +// Implements the ECDSA family of signing methods signing methods +type SigningMethodECDSA struct { + Name string + Hash crypto.Hash + KeySize int + CurveBits int +} + +// Specific instances for EC256 and company +var ( + SigningMethodES256 *SigningMethodECDSA + SigningMethodES384 *SigningMethodECDSA + SigningMethodES512 *SigningMethodECDSA +) + +func init() { + // ES256 + SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} + RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { + return SigningMethodES256 + }) + + // ES384 + SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} + RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { + return SigningMethodES384 + }) + + // ES512 + SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} + RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { + return SigningMethodES512 + }) +} + +func (m *SigningMethodECDSA) Alg() string { + return m.Name +} + +// Implements the Verify method from SigningMethod +// For this verify method, key must be an ecdsa.PublicKey struct +func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + // Get the key + var ecdsaKey *ecdsa.PublicKey + switch k := key.(type) { + case *ecdsa.PublicKey: + ecdsaKey = k + default: + return ErrInvalidKeyType + } + + if len(sig) != 2*m.KeySize { + return ErrECDSAVerification + } + + r := big.NewInt(0).SetBytes(sig[:m.KeySize]) + s := big.NewInt(0).SetBytes(sig[m.KeySize:]) + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Verify the signature + if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true { + return nil + } else { + return ErrECDSAVerification + } +} + +// Implements the Sign method from SigningMethod +// For this signing method, key must be an ecdsa.PrivateKey struct +func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { + // Get the key + var ecdsaKey *ecdsa.PrivateKey + switch k := key.(type) { + case *ecdsa.PrivateKey: + ecdsaKey = k + default: + return "", ErrInvalidKeyType + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return r, s + if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { + curveBits := ecdsaKey.Curve.Params().BitSize + + if m.CurveBits != curveBits { + return "", ErrInvalidKey + } + + keyBytes := curveBits / 8 + if curveBits%8 > 0 { + keyBytes += 1 + } + + // We serialize the outpus (r and s) into big-endian byte arrays and pad + // them with zeros on the left to make sure the sizes work out. Both arrays + // must be keyBytes long, and the output must be 2*keyBytes long. + rBytes := r.Bytes() + rBytesPadded := make([]byte, keyBytes) + copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) + + sBytes := s.Bytes() + sBytesPadded := make([]byte, keyBytes) + copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) + + out := append(rBytesPadded, sBytesPadded...) + + return EncodeSegment(out), nil + } else { + return "", err + } +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go b/src/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go new file mode 100644 index 000000000..d19624b72 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go @@ -0,0 +1,67 @@ +package jwt + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key") + ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") +) + +// Parse PEM encoded Elliptic Curve Private Key Structure +func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { + return nil, err + } + + var pkey *ecdsa.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { + return nil, ErrNotECPrivateKey + } + + return pkey, nil +} + +// Parse PEM encoded PKCS1 or PKCS8 public key +func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + if cert, err := x509.ParseCertificate(block.Bytes); err == nil { + parsedKey = cert.PublicKey + } else { + return nil, err + } + } + + var pkey *ecdsa.PublicKey + var ok bool + if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { + return nil, ErrNotECPublicKey + } + + return pkey, nil +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/errors.go b/src/vendor/github.com/dgrijalva/jwt-go/errors.go new file mode 100644 index 000000000..662df19d4 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/errors.go @@ -0,0 +1,63 @@ +package jwt + +import ( + "errors" +) + +// Error constants +var ( + ErrInvalidKey = errors.New("key is invalid") + ErrInvalidKeyType = errors.New("key is of invalid type") + ErrHashUnavailable = errors.New("the requested hash function is unavailable") +) + +// The errors that might occur when parsing and validating a token +const ( + ValidationErrorMalformed uint32 = 1 << iota // Token is malformed + ValidationErrorUnverifiable // Token could not be verified because of signing problems + ValidationErrorSignatureInvalid // Signature validation failed + + // Standard Claim validation errors + ValidationErrorAudience // AUD validation failed + ValidationErrorExpired // EXP validation failed + ValidationErrorIssuedAt // IAT validation failed + ValidationErrorIssuer // ISS validation failed + ValidationErrorNotValidYet // NBF validation failed + ValidationErrorId // JTI validation failed + ValidationErrorClaimsInvalid // Generic claims validation error +) + +// Helper for constructing a ValidationError with a string error message +func NewValidationError(errorText string, errorFlags uint32) *ValidationError { + return &ValidationError{ + text: errorText, + Errors: errorFlags, + } +} + +// The error from Parse if token is not valid +type ValidationError struct { + Inner error // stores the error returned by external dependencies, i.e.: KeyFunc + Errors uint32 // bitfield. see ValidationError... constants + text string // errors that do not have a valid error just have text +} + +// Validation error is an error type +func (e ValidationError) Error() string { + if e.Inner != nil { + return e.Inner.Error() + } else if e.text != "" { + return e.text + } else { + return "token is invalid" + } + return e.Inner.Error() +} + +// No errors +func (e *ValidationError) valid() bool { + if e.Errors > 0 { + return false + } + return true +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/hmac.go b/src/vendor/github.com/dgrijalva/jwt-go/hmac.go new file mode 100644 index 000000000..c22991925 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/hmac.go @@ -0,0 +1,94 @@ +package jwt + +import ( + "crypto" + "crypto/hmac" + "errors" +) + +// Implements the HMAC-SHA family of signing methods signing methods +type SigningMethodHMAC struct { + Name string + Hash crypto.Hash +} + +// Specific instances for HS256 and company +var ( + SigningMethodHS256 *SigningMethodHMAC + SigningMethodHS384 *SigningMethodHMAC + SigningMethodHS512 *SigningMethodHMAC + ErrSignatureInvalid = errors.New("signature is invalid") +) + +func init() { + // HS256 + SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256} + RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod { + return SigningMethodHS256 + }) + + // HS384 + SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384} + RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod { + return SigningMethodHS384 + }) + + // HS512 + SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512} + RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod { + return SigningMethodHS512 + }) +} + +func (m *SigningMethodHMAC) Alg() string { + return m.Name +} + +// Verify the signature of HSXXX tokens. Returns nil if the signature is valid. +func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { + // Verify the key is the right type + keyBytes, ok := key.([]byte) + if !ok { + return ErrInvalidKeyType + } + + // Decode signature, for comparison + sig, err := DecodeSegment(signature) + if err != nil { + return err + } + + // Can we use the specified hashing method? + if !m.Hash.Available() { + return ErrHashUnavailable + } + + // This signing method is symmetric, so we validate the signature + // by reproducing the signature from the signing string and key, then + // comparing that against the provided signature. + hasher := hmac.New(m.Hash.New, keyBytes) + hasher.Write([]byte(signingString)) + if !hmac.Equal(sig, hasher.Sum(nil)) { + return ErrSignatureInvalid + } + + // No validation errors. Signature is good. + return nil +} + +// Implements the Sign method from SigningMethod for this signing method. +// Key must be []byte +func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { + if keyBytes, ok := key.([]byte); ok { + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := hmac.New(m.Hash.New, keyBytes) + hasher.Write([]byte(signingString)) + + return EncodeSegment(hasher.Sum(nil)), nil + } + + return "", ErrInvalidKey +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/map_claims.go b/src/vendor/github.com/dgrijalva/jwt-go/map_claims.go new file mode 100644 index 000000000..291213c46 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/map_claims.go @@ -0,0 +1,94 @@ +package jwt + +import ( + "encoding/json" + "errors" + // "fmt" +) + +// Claims type that uses the map[string]interface{} for JSON decoding +// This is the default claims type if you don't supply one +type MapClaims map[string]interface{} + +// Compares the aud claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyAudience(cmp string, req bool) bool { + aud, _ := m["aud"].(string) + return verifyAud(aud, cmp, req) +} + +// Compares the exp claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { + switch exp := m["exp"].(type) { + case float64: + return verifyExp(int64(exp), cmp, req) + case json.Number: + v, _ := exp.Int64() + return verifyExp(v, cmp, req) + } + return req == false +} + +// Compares the iat claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { + switch iat := m["iat"].(type) { + case float64: + return verifyIat(int64(iat), cmp, req) + case json.Number: + v, _ := iat.Int64() + return verifyIat(v, cmp, req) + } + return req == false +} + +// Compares the iss claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyIssuer(cmp string, req bool) bool { + iss, _ := m["iss"].(string) + return verifyIss(iss, cmp, req) +} + +// Compares the nbf claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { + switch nbf := m["nbf"].(type) { + case float64: + return verifyNbf(int64(nbf), cmp, req) + case json.Number: + v, _ := nbf.Int64() + return verifyNbf(v, cmp, req) + } + return req == false +} + +// Validates time based claims "exp, iat, nbf". +// There is no accounting for clock skew. +// As well, if any of the above claims are not in the token, it will still +// be considered a valid claim. +func (m MapClaims) Valid() error { + vErr := new(ValidationError) + now := TimeFunc().Unix() + + if m.VerifyExpiresAt(now, false) == false { + vErr.Inner = errors.New("Token is expired") + vErr.Errors |= ValidationErrorExpired + } + + if m.VerifyIssuedAt(now, false) == false { + vErr.Inner = errors.New("Token used before issued") + vErr.Errors |= ValidationErrorIssuedAt + } + + if m.VerifyNotBefore(now, false) == false { + vErr.Inner = errors.New("Token is not valid yet") + vErr.Errors |= ValidationErrorNotValidYet + } + + if vErr.valid() { + return nil + } + + return vErr +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/none.go b/src/vendor/github.com/dgrijalva/jwt-go/none.go new file mode 100644 index 000000000..f04d189d0 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/none.go @@ -0,0 +1,52 @@ +package jwt + +// Implements the none signing method. This is required by the spec +// but you probably should never use it. +var SigningMethodNone *signingMethodNone + +const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed" + +var NoneSignatureTypeDisallowedError error + +type signingMethodNone struct{} +type unsafeNoneMagicConstant string + +func init() { + SigningMethodNone = &signingMethodNone{} + NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid) + + RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod { + return SigningMethodNone + }) +} + +func (m *signingMethodNone) Alg() string { + return "none" +} + +// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key +func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) { + // Key must be UnsafeAllowNoneSignatureType to prevent accidentally + // accepting 'none' signing method + if _, ok := key.(unsafeNoneMagicConstant); !ok { + return NoneSignatureTypeDisallowedError + } + // If signing method is none, signature must be an empty string + if signature != "" { + return NewValidationError( + "'none' signing method with non-empty signature", + ValidationErrorSignatureInvalid, + ) + } + + // Accept 'none' signing method. + return nil +} + +// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key +func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) { + if _, ok := key.(unsafeNoneMagicConstant); ok { + return "", nil + } + return "", NoneSignatureTypeDisallowedError +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/parser.go b/src/vendor/github.com/dgrijalva/jwt-go/parser.go new file mode 100644 index 000000000..7020c52a1 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/parser.go @@ -0,0 +1,128 @@ +package jwt + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +type Parser struct { + ValidMethods []string // If populated, only these methods will be considered valid + UseJSONNumber bool // Use JSON Number format in JSON decoder +} + +// Parse, validate, and return a token. +// keyFunc will receive the parsed token and should return the key for validating. +// If everything is kosher, err will be nil +func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { + return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) +} + +func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { + parts := strings.Split(tokenString, ".") + if len(parts) != 3 { + return nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) + } + + var err error + token := &Token{Raw: tokenString} + + // parse Header + var headerBytes []byte + if headerBytes, err = DecodeSegment(parts[0]); err != nil { + if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { + return token, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed) + } + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + if err = json.Unmarshal(headerBytes, &token.Header); err != nil { + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + + // parse Claims + var claimBytes []byte + token.Claims = claims + + if claimBytes, err = DecodeSegment(parts[1]); err != nil { + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) + if p.UseJSONNumber { + dec.UseNumber() + } + // JSON Decode. Special case for map type to avoid weird pointer behavior + if c, ok := token.Claims.(MapClaims); ok { + err = dec.Decode(&c) + } else { + err = dec.Decode(&claims) + } + // Handle decode error + if err != nil { + return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + + // Lookup signature method + if method, ok := token.Header["alg"].(string); ok { + if token.Method = GetSigningMethod(method); token.Method == nil { + return token, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable) + } + } else { + return token, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable) + } + + // Verify signing method is in the required set + if p.ValidMethods != nil { + var signingMethodValid = false + var alg = token.Method.Alg() + for _, m := range p.ValidMethods { + if m == alg { + signingMethodValid = true + break + } + } + if !signingMethodValid { + // signing method is not in the listed set + return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid) + } + } + + // Lookup key + var key interface{} + if keyFunc == nil { + // keyFunc was not provided. short circuiting validation + return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable) + } + if key, err = keyFunc(token); err != nil { + // keyFunc returned an error + return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} + } + + vErr := &ValidationError{} + + // Validate Claims + if err := token.Claims.Valid(); err != nil { + + // If the Claims Valid returned an error, check if it is a validation error, + // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set + if e, ok := err.(*ValidationError); !ok { + vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid} + } else { + vErr = e + } + } + + // Perform validation + token.Signature = parts[2] + if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { + vErr.Inner = err + vErr.Errors |= ValidationErrorSignatureInvalid + } + + if vErr.valid() { + token.Valid = true + return token, nil + } + + return token, vErr +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/rsa.go b/src/vendor/github.com/dgrijalva/jwt-go/rsa.go new file mode 100644 index 000000000..0ae0b1984 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/rsa.go @@ -0,0 +1,100 @@ +package jwt + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" +) + +// Implements the RSA family of signing methods signing methods +type SigningMethodRSA struct { + Name string + Hash crypto.Hash +} + +// Specific instances for RS256 and company +var ( + SigningMethodRS256 *SigningMethodRSA + SigningMethodRS384 *SigningMethodRSA + SigningMethodRS512 *SigningMethodRSA +) + +func init() { + // RS256 + SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256} + RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod { + return SigningMethodRS256 + }) + + // RS384 + SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384} + RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod { + return SigningMethodRS384 + }) + + // RS512 + SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512} + RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod { + return SigningMethodRS512 + }) +} + +func (m *SigningMethodRSA) Alg() string { + return m.Name +} + +// Implements the Verify method from SigningMethod +// For this signing method, must be an rsa.PublicKey structure. +func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + var rsaKey *rsa.PublicKey + var ok bool + + if rsaKey, ok = key.(*rsa.PublicKey); !ok { + return ErrInvalidKeyType + } + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Verify the signature + return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) +} + +// Implements the Sign method from SigningMethod +// For this signing method, must be an rsa.PrivateKey structure. +func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { + var rsaKey *rsa.PrivateKey + var ok bool + + // Validate type of key + if rsaKey, ok = key.(*rsa.PrivateKey); !ok { + return "", ErrInvalidKey + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return the encoded bytes + if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil { + return EncodeSegment(sigBytes), nil + } else { + return "", err + } +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go b/src/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go new file mode 100644 index 000000000..10ee9db8a --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/rsa_pss.go @@ -0,0 +1,126 @@ +// +build go1.4 + +package jwt + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" +) + +// Implements the RSAPSS family of signing methods signing methods +type SigningMethodRSAPSS struct { + *SigningMethodRSA + Options *rsa.PSSOptions +} + +// Specific instances for RS/PS and company +var ( + SigningMethodPS256 *SigningMethodRSAPSS + SigningMethodPS384 *SigningMethodRSAPSS + SigningMethodPS512 *SigningMethodRSAPSS +) + +func init() { + // PS256 + SigningMethodPS256 = &SigningMethodRSAPSS{ + &SigningMethodRSA{ + Name: "PS256", + Hash: crypto.SHA256, + }, + &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA256, + }, + } + RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { + return SigningMethodPS256 + }) + + // PS384 + SigningMethodPS384 = &SigningMethodRSAPSS{ + &SigningMethodRSA{ + Name: "PS384", + Hash: crypto.SHA384, + }, + &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA384, + }, + } + RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { + return SigningMethodPS384 + }) + + // PS512 + SigningMethodPS512 = &SigningMethodRSAPSS{ + &SigningMethodRSA{ + Name: "PS512", + Hash: crypto.SHA512, + }, + &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA512, + }, + } + RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { + return SigningMethodPS512 + }) +} + +// Implements the Verify method from SigningMethod +// For this verify method, key must be an rsa.PublicKey struct +func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + var rsaKey *rsa.PublicKey + switch k := key.(type) { + case *rsa.PublicKey: + rsaKey = k + default: + return ErrInvalidKey + } + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options) +} + +// Implements the Sign method from SigningMethod +// For this signing method, key must be an rsa.PrivateKey struct +func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { + var rsaKey *rsa.PrivateKey + + switch k := key.(type) { + case *rsa.PrivateKey: + rsaKey = k + default: + return "", ErrInvalidKeyType + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return the encoded bytes + if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { + return EncodeSegment(sigBytes), nil + } else { + return "", err + } +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go b/src/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go new file mode 100644 index 000000000..213a90dbb --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/rsa_utils.go @@ -0,0 +1,69 @@ +package jwt + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key") + ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key") + ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key") +) + +// Parse PEM encoded PKCS1 or PKCS8 private key +func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + var parsedKey interface{} + if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + } + + var pkey *rsa.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { + return nil, ErrNotRSAPrivateKey + } + + return pkey, nil +} + +// Parse PEM encoded PKCS1 or PKCS8 public key +func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + if cert, err := x509.ParseCertificate(block.Bytes); err == nil { + parsedKey = cert.PublicKey + } else { + return nil, err + } + } + + var pkey *rsa.PublicKey + var ok bool + if pkey, ok = parsedKey.(*rsa.PublicKey); !ok { + return nil, ErrNotRSAPublicKey + } + + return pkey, nil +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/signing_method.go b/src/vendor/github.com/dgrijalva/jwt-go/signing_method.go new file mode 100644 index 000000000..ed1f212b2 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/signing_method.go @@ -0,0 +1,35 @@ +package jwt + +import ( + "sync" +) + +var signingMethods = map[string]func() SigningMethod{} +var signingMethodLock = new(sync.RWMutex) + +// Implement SigningMethod to add new methods for signing or verifying tokens. +type SigningMethod interface { + Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid + Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error + Alg() string // returns the alg identifier for this method (example: 'HS256') +} + +// Register the "alg" name and a factory function for signing method. +// This is typically done during init() in the method's implementation +func RegisterSigningMethod(alg string, f func() SigningMethod) { + signingMethodLock.Lock() + defer signingMethodLock.Unlock() + + signingMethods[alg] = f +} + +// Get a signing method from an "alg" string +func GetSigningMethod(alg string) (method SigningMethod) { + signingMethodLock.RLock() + defer signingMethodLock.RUnlock() + + if methodF, ok := signingMethods[alg]; ok { + method = methodF() + } + return +} diff --git a/src/vendor/github.com/dgrijalva/jwt-go/token.go b/src/vendor/github.com/dgrijalva/jwt-go/token.go new file mode 100644 index 000000000..d637e0867 --- /dev/null +++ b/src/vendor/github.com/dgrijalva/jwt-go/token.go @@ -0,0 +1,108 @@ +package jwt + +import ( + "encoding/base64" + "encoding/json" + "strings" + "time" +) + +// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time). +// You can override it to use another time value. This is useful for testing or if your +// server uses a different time zone than your tokens. +var TimeFunc = time.Now + +// Parse methods use this callback function to supply +// the key for verification. The function receives the parsed, +// but unverified Token. This allows you to use properties in the +// Header of the token (such as `kid`) to identify which key to use. +type Keyfunc func(*Token) (interface{}, error) + +// A JWT Token. Different fields will be used depending on whether you're +// creating or parsing/verifying a token. +type Token struct { + Raw string // The raw token. Populated when you Parse a token + Method SigningMethod // The signing method used or to be used + Header map[string]interface{} // The first segment of the token + Claims Claims // The second segment of the token + Signature string // The third segment of the token. Populated when you Parse a token + Valid bool // Is the token valid? Populated when you Parse/Verify a token +} + +// Create a new Token. Takes a signing method +func New(method SigningMethod) *Token { + return NewWithClaims(method, MapClaims{}) +} + +func NewWithClaims(method SigningMethod, claims Claims) *Token { + return &Token{ + Header: map[string]interface{}{ + "typ": "JWT", + "alg": method.Alg(), + }, + Claims: claims, + Method: method, + } +} + +// Get the complete, signed token +func (t *Token) SignedString(key interface{}) (string, error) { + var sig, sstr string + var err error + if sstr, err = t.SigningString(); err != nil { + return "", err + } + if sig, err = t.Method.Sign(sstr, key); err != nil { + return "", err + } + return strings.Join([]string{sstr, sig}, "."), nil +} + +// Generate the signing string. This is the +// most expensive part of the whole deal. Unless you +// need this for something special, just go straight for +// the SignedString. +func (t *Token) SigningString() (string, error) { + var err error + parts := make([]string, 2) + for i, _ := range parts { + var jsonValue []byte + if i == 0 { + if jsonValue, err = json.Marshal(t.Header); err != nil { + return "", err + } + } else { + if jsonValue, err = json.Marshal(t.Claims); err != nil { + return "", err + } + } + + parts[i] = EncodeSegment(jsonValue) + } + return strings.Join(parts, "."), nil +} + +// Parse, validate, and return a token. +// keyFunc will receive the parsed token and should return the key for validating. +// If everything is kosher, err will be nil +func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { + return new(Parser).Parse(tokenString, keyFunc) +} + +func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { + return new(Parser).ParseWithClaims(tokenString, claims, keyFunc) +} + +// Encode JWT specific base64url encoding with padding stripped +func EncodeSegment(seg []byte) string { + return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=") +} + +// Decode JWT specific base64url encoding with padding stripped +func DecodeSegment(seg string) ([]byte, error) { + if l := len(seg) % 4; l > 0 { + seg += strings.Repeat("=", 4-l) + } + + return base64.URLEncoding.DecodeString(seg) +} diff --git a/src/vendor/github.com/docker/distribution/AUTHORS b/src/vendor/github.com/docker/distribution/AUTHORS index 9e80e062b..252ff8aa2 100644 --- a/src/vendor/github.com/docker/distribution/AUTHORS +++ b/src/vendor/github.com/docker/distribution/AUTHORS @@ -1,6 +1,8 @@ +a-palchikov Aaron Lehmann Aaron Schlesinger Aaron Vinson +Adam Duke Adam Enger Adrian Mouat Ahmet Alp Balkan @@ -19,6 +21,7 @@ Anis Elleuch Anton Tiurin Antonio Mercado Antonio Murdaca +Anusha Ragunathan Arien Holthuizen Arnaud Porterie Arthur Baars @@ -26,12 +29,16 @@ Asuka Suzuki Avi Miller Ayose Cazorla BadZen +Ben Bodenmiller Ben Firshman bin liu Brian Bland burnettk Carson A +Cezar Sa Espinola +Charles Smith Chris Dillon +cuiwei13 cyli Daisuke Fujita Daniel Huhn @@ -48,11 +55,14 @@ Diogo Mónica DJ Enriquez Donald Huang Doug Davis +Edgar Lee Eric Yang +Fabio Berchtold Fabio Huser farmerworking Felix Yan Florentin Raud +Frank Chen Frederick F. Kautz IV gabriell nascimento Gleb Schukin @@ -64,16 +74,23 @@ HuKeping Ian Babrou igayoso Jack Griffin +James Findley Jason Freidman +Jason Heiss Jeff Nickoloff +Jess Frazelle Jessie Frazelle jhaohai Jianqing Wang +Jihoon Chung +Joao Fernandes +John Mulhausen John Starks Jon Johnson Jon Poler Jonathan Boulle Jordan Liggitt +Josh Chorlton Josh Hawn Julien Fernandez Ke Xu @@ -84,22 +101,30 @@ Kenny Leung Li Yi Liu Hua liuchang0812 +Lloyd Ramey Louis Kottmann Luke Carpenter +Marcus Martins Mary Anthony Matt Bentley Matt Duch Matt Moore Matt Robenolt +Matthew Green Michael Prokop Michal Minar +Michal Minář +Mike Brown Miquel Sabaté +Misty Stanley-Jones +Misty Stanley-Jones Morgan Bauer moxiegirl Nathan Sullivan nevermosby Nghia Tran Nikita Tarasov +Noah Treuhaft Nuutti Kotivuori Oilbeater Olivier Gambier @@ -108,17 +133,23 @@ Omer Cohen Patrick Devine Phil Estes Philip Misiowiec +Pierre-Yves Ritschard +Qiao Anran +Randy Barlow Richard Scothern Rodolfo Carvalho Rusty Conover Sean Boran Sebastiaan van Stijn +Sebastien Coavoux Serge Dubrouski Sharif Nassar Shawn Falkner-Horine Shreyas Karnik Simon Thulbourn +spacexnice Spencer Rinehart +Stan Hu Stefan Majewsky Stefan Weil Stephen J Day @@ -134,6 +165,8 @@ Tonis Tiigi Tony Holdstock-Brown Trevor Pounds Troels Thomsen +Victor Vieux +Victoria Bialas Vincent Batts Vincent Demeester Vincent Giersch @@ -142,6 +175,8 @@ weiyuan.yl xg.song xiekeyang Yann ROBERT +yaoyao.xyy +yuexiao-wang yuzou zhouhaibing089 姜继忠 diff --git a/src/vendor/github.com/docker/distribution/BUILDING.md b/src/vendor/github.com/docker/distribution/BUILDING.md index d9577022b..2d5a10119 100644 --- a/src/vendor/github.com/docker/distribution/BUILDING.md +++ b/src/vendor/github.com/docker/distribution/BUILDING.md @@ -11,7 +11,7 @@ Most people should use the [official Registry docker image](https://hub.docker.c People looking for advanced operational use cases might consider rolling their own image with a custom Dockerfile inheriting `FROM registry:2`. -OS X users who want to run natively can do so following [the instructions here](osx-setup-guide.md). +OS X users who want to run natively can do so following [the instructions here](https://github.com/docker/docker.github.io/blob/master/registry/recipes/osx-setup-guide.md). ### Gotchas diff --git a/src/vendor/github.com/docker/distribution/CHANGELOG.md b/src/vendor/github.com/docker/distribution/CHANGELOG.md index 3445c090c..e7b16b3c2 100644 --- a/src/vendor/github.com/docker/distribution/CHANGELOG.md +++ b/src/vendor/github.com/docker/distribution/CHANGELOG.md @@ -1,9 +1,82 @@ # Changelog +## 2.6.0 (2017-01-18) + +#### Storage +- S3: fixed bug in delete due to read-after-write inconsistency +- S3: allow EC2 IAM roles to be used when authorizing region endpoints +- S3: add Object ACL Support +- S3: fix delete method's notion of subpaths +- S3: use multipart upload API in `Move` method for performance +- S3: add v2 signature signing for legacy S3 clones +- Swift: add simple heuristic to detect incomplete DLOs during read ops +- Swift: support different user and tenant domains +- Swift: bulk deletes in chunks +- Aliyun OSS: fix delete method's notion of subpaths +- Aliyun OSS: optimize data copy after upload finishes +- Azure: close leaking response body +- Fix storage drivers dropping non-EOF errors when listing repositories +- Compare path properly when listing repositories in catalog +- Add a foreign layer URL host whitelist +- Improve catalog enumerate runtime + +#### Registry +- Export `storage.CreateOptions` in top-level package +- Enable notifications to endpoints that use self-signed certificates +- Properly validate multi-URL foreign layers +- Add control over validation of URLs in pushed manifests +- Proxy mode: fix socket leak when pull is cancelled +- Tag service: properly handle error responses on HEAD request +- Support for custom authentication URL in proxying registry +- Add configuration option to disable access logging +- Add notification filtering by target media type +- Manifest: `References()` returns all children +- Honor `X-Forwarded-Port` and Forwarded headers +- Reference: Preserve tag and digest in With* functions +- Add policy configuration for enforcing repository classes + +#### Client +- Changes the client Tags `All()` method to follow links +- Allow registry clients to connect via HTTP2 +- Better handling of OAuth errors in client + +#### Spec +- Manifest: clarify relationship between urls and foreign layers +- Authorization: add support for repository classes + +#### Manifest +- Override media type returned from `Stat()` for existing manifests +- Add plugin mediatype to distribution manifest + +#### Docs +- Document `TOOMANYREQUESTS` error code +- Document required Let's Encrypt port +- Improve documentation around implementation of OAuth2 +- Improve documentation for configuration + +#### Auth +- Add support for registry type in scope +- Add support for using v2 ping challenges for v1 +- Add leeway to JWT `nbf` and `exp` checking +- htpasswd: dynamically parse htpasswd file +- Fix missing auth headers with PATCH HTTP request when pushing to default port + +#### Dockerfile +- Update to go1.7 +- Reorder Dockerfile steps for better layer caching + +#### Notes + +Documentation has moved to the documentation repository at +`github.com/docker/docker.github.io/tree/master/registry` + +The registry is go 1.7 compliant, and passes newer, more restrictive `lint` and `vet` ing. + + ## 2.5.0 (2016-06-14) -### Storage -- Ensure uploads directory is cleaned after upload is commited +#### Storage +- Ensure uploads directory is cleaned after upload is committed - Add ability to cap concurrent operations in filesystem driver - S3: Add 'us-gov-west-1' to the valid region list - Swift: Handle ceph not returning Last-Modified header for HEAD requests @@ -23,13 +96,13 @@ - Update the auth spec scope grammar to reflect the fact that hostnames are optionally supported - Clarify API documentation around catalog fetch behavior -### API +#### API - Support returning HTTP 429 (Too Many Requests) -### Documentation +#### Documentation - Update auth documentation examples to show "expires in" as int -### Docker Image +#### Docker Image - Use Alpine Linux as base image diff --git a/src/vendor/github.com/docker/distribution/Dockerfile b/src/vendor/github.com/docker/distribution/Dockerfile index fa9cd4627..426954a11 100644 --- a/src/vendor/github.com/docker/distribution/Dockerfile +++ b/src/vendor/github.com/docker/distribution/Dockerfile @@ -1,15 +1,15 @@ -FROM golang:1.6-alpine +FROM golang:1.7-alpine ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution ENV DOCKER_BUILDTAGS include_oss include_gcs +RUN set -ex \ + && apk add --no-cache make git + WORKDIR $DISTRIBUTION_DIR COPY . $DISTRIBUTION_DIR COPY cmd/registry/config-dev.yml /etc/docker/registry/config.yml -RUN set -ex \ - && apk add --no-cache make git - RUN make PREFIX=/go clean binaries VOLUME ["/var/lib/registry"] diff --git a/src/vendor/github.com/docker/distribution/Makefile b/src/vendor/github.com/docker/distribution/Makefile index a0602d0b2..47b8f1d0b 100644 --- a/src/vendor/github.com/docker/distribution/Makefile +++ b/src/vendor/github.com/docker/distribution/Makefile @@ -13,7 +13,7 @@ endif GO_LDFLAGS=-ldflags "-X `go list ./version`.Version=$(VERSION)" -.PHONY: clean all fmt vet lint build test binaries +.PHONY: all build binaries clean dep-restore dep-save dep-validate fmt lint test test-full vet .DEFAULT: all all: fmt vet lint build test binaries @@ -27,22 +27,25 @@ version/version.go: # Required for go 1.5 to build GO15VENDOREXPERIMENT := 1 +# Go files +GOFILES=$(shell find . -type f -name '*.go') + # Package list -PKGS := $(shell go list -tags "${DOCKER_BUILDTAGS}" ./... | grep -v ^github.com/docker/distribution/vendor/) +PKGS=$(shell go list -tags "${DOCKER_BUILDTAGS}" ./... | grep -v ^github.com/docker/distribution/vendor/) # Resolving binary dependencies for specific targets -GOLINT := $(shell which golint || echo '') -GODEP := $(shell which godep || echo '') +GOLINT=$(shell which golint || echo '') +GODEP=$(shell which godep || echo '') -${PREFIX}/bin/registry: $(wildcard **/*.go) +${PREFIX}/bin/registry: $(GOFILES) @echo "+ $@" @go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/registry -${PREFIX}/bin/digest: $(wildcard **/*.go) +${PREFIX}/bin/digest: $(GOFILES) @echo "+ $@" @go build -tags "${DOCKER_BUILDTAGS}" -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/digest -${PREFIX}/bin/registry-api-descriptor-template: $(wildcard **/*.go) +${PREFIX}/bin/registry-api-descriptor-template: $(GOFILES) @echo "+ $@" @go build -o $@ ${GO_LDFLAGS} ${GO_GCFLAGS} ./cmd/registry-api-descriptor-template diff --git a/src/vendor/github.com/docker/distribution/README.md b/src/vendor/github.com/docker/distribution/README.md index d35bcb682..a6e8db0fb 100644 --- a/src/vendor/github.com/docker/distribution/README.md +++ b/src/vendor/github.com/docker/distribution/README.md @@ -19,7 +19,7 @@ This repository contains the following components: | **registry** | An implementation of the [Docker Registry HTTP API V2](docs/spec/api.md) for use with docker 1.6+. | | **libraries** | A rich set of libraries for interacting with distribution components. Please see [godoc](https://godoc.org/github.com/docker/distribution) for details. **Note**: These libraries are **unstable**. | | **specifications** | _Distribution_ related specifications are available in [docs/spec](docs/spec) | -| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/index.md) related just to the registry. | +| **documentation** | Docker's full documentation set is available at [docs.docker.com](https://docs.docker.com). This repository [contains the subset](docs/) related just to the registry. | ### How does this integrate with Docker engine? @@ -60,15 +60,15 @@ For information on upcoming functionality, please see [ROADMAP.md](ROADMAP.md). By default, Docker users pull images from Docker's public registry instance. [Installing Docker](https://docs.docker.com/engine/installation/) gives users this ability. Users can also push images to a repository on Docker's public registry, -if they have a [Docker Hub](https://hub.docker.com/) account. +if they have a [Docker Hub](https://hub.docker.com/) account. For some users and even companies, this default behavior is sufficient. For -others, it is not. +others, it is not. For example, users with their own software products may want to maintain a registry for private, company images. Also, you may wish to deploy your own image repository for images used to test or in continuous integration. For these -use cases and others, [deploying your own registry instance](docs/deploying.md) +use cases and others, [deploying your own registry instance](https://github.com/docker/docker.github.io/blob/master/registry/deploying.md) may be the better choice. ### Migration to Registry 2.0 @@ -83,7 +83,7 @@ created. For more information see [docker/migrator] Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute issues, fixes, and patches to this project. If you are contributing code, see -the instructions for [building a development environment](docs/recipes/building.md). +the instructions for [building a development environment](BUILDING.md). ## Support diff --git a/src/vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md b/src/vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md new file mode 100644 index 000000000..49235cecd --- /dev/null +++ b/src/vendor/github.com/docker/distribution/RELEASE-CHECKLIST.md @@ -0,0 +1,36 @@ +## Registry Release Checklist + +10. Compile release notes detailing features and since the last release. Update the `CHANGELOG.md` file. + +20. Update the version file: `https://github.com/docker/distribution/blob/master/version/version.go` + +30. Update the `MAINTAINERS` (if necessary), `AUTHORS` and `.mailmap` files. + + ``` +make AUTHORS +``` + +40. Create a signed tag. + + Distribution uses semantic versioning. Tags are of the format `vx.y.z[-rcn]` +You will need PGP installed and a PGP key which has been added to your Github account. The comment for the tag should include the release notes. + +50. Push the signed tag + +60. Create a new [release](https://github.com/docker/distribution/releases). In the case of a release candidate, tick the `pre-release` checkbox. + +70. Update the registry binary in [distribution library image repo](https://github.com/docker/distribution-library-image) by running the update script and opening a pull request. + +80. Update the official image. Add the new version in the [official images repo](https://github.com/docker-library/official-images) by appending a new version to the `registry/registry` file with the git hash pointed to by the signed tag. Update the major version to point to the latest version and the minor version to point to new patch release if necessary. +e.g. to release `2.3.1` + + `2.3.1 (new)` + + `2.3.0 -> 2.3.0` can be removed + + `2 -> 2.3.1` + + `2.3 -> 2.3.1` + +90. Build a new distribution/registry image on [Docker hub](https://hub.docker.com/u/distribution/dashboard) by adding a new automated build with the new tag and re-building the images. + diff --git a/src/vendor/github.com/docker/distribution/blobs.go b/src/vendor/github.com/docker/distribution/blobs.go index d12533011..1f91ae21e 100644 --- a/src/vendor/github.com/docker/distribution/blobs.go +++ b/src/vendor/github.com/docker/distribution/blobs.go @@ -192,6 +192,18 @@ type BlobCreateOption interface { Apply(interface{}) error } +// CreateOptions is a collection of blob creation modifiers relevant to general +// blob storage intended to be configured by the BlobCreateOption.Apply method. +type CreateOptions struct { + Mount struct { + ShouldMount bool + From reference.Canonical + // Stat allows to pass precalculated descriptor to link and return. + // Blob access check will be skipped if set. + Stat *Descriptor + } +} + // BlobWriter provides a handle for inserting data into a blob store. // Instances should be obtained from BlobWriteService.Writer and // BlobWriteService.Resume. If supported by the store, a writer can be diff --git a/src/vendor/github.com/docker/distribution/circle.yml b/src/vendor/github.com/docker/distribution/circle.yml index 3d1ffd2f0..61f8be0cb 100644 --- a/src/vendor/github.com/docker/distribution/circle.yml +++ b/src/vendor/github.com/docker/distribution/circle.yml @@ -8,7 +8,7 @@ machine: post: # go - - gvm install go1.6 --prefer-binary --name=stable + - gvm install go1.7 --prefer-binary --name=stable environment: # Convenient shortcuts to "common" locations @@ -49,9 +49,10 @@ test: # - gvm use old && go version - gvm use stable && go version + # todo(richard): replace with a more robust vendoring solution. Removed due to a fundamental disagreement in godep philosophies. # Ensure validation of dependencies - - gvm use stable && if test -n "`git diff --stat=1000 master | grep -Ei \"vendor|godeps\"`"; then make dep-validate; fi: - pwd: $BASE_STABLE + # - gvm use stable && if test -n "`git diff --stat=1000 master | grep -Ei \"vendor|godeps\"`"; then make dep-validate; fi: + # pwd: $BASE_STABLE # First thing: build everything. This will catch compile errors, and it's # also necessary for go vet to work properly (see #807). @@ -73,16 +74,19 @@ test: override: # Test stable, and report - gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | xargs -L 1 -I{} bash -c 'export PACKAGE={}; godep go test -tags "$DOCKER_BUILDTAGS" -test.short -coverprofile=$GOPATH/src/$PACKAGE/coverage.out -coverpkg=$(./coverpkg.sh $PACKAGE $ROOT_PACKAGE) $PACKAGE': - timeout: 600 + timeout: 1000 pwd: $BASE_STABLE + # Test stable with race + - gvm use stable; export ROOT_PACKAGE=$(go list .); go list -tags "$DOCKER_BUILDTAGS" ./... | grep -v "/vendor/" | grep -v "registry/handlers" | grep -v "registry/storage/driver" | xargs -L 1 -I{} bash -c 'export PACKAGE={}; godep go test -race -tags "$DOCKER_BUILDTAGS" -test.short $PACKAGE': + timeout: 1000 + pwd: $BASE_STABLE post: # Report to codecov - bash <(curl -s https://codecov.io/bash): pwd: $BASE_STABLE ## Notes - # Disabled the -race detector due to massive memory usage. # Do we want these as well? # - go get code.google.com/p/go.tools/cmd/goimports # - test -z "$(goimports -l -w ./... | tee /dev/stderr)" diff --git a/src/vendor/github.com/docker/distribution/context/http.go b/src/vendor/github.com/docker/distribution/context/http.go index 2cb1d0417..7fe9b8ab0 100644 --- a/src/vendor/github.com/docker/distribution/context/http.go +++ b/src/vendor/github.com/docker/distribution/context/http.go @@ -103,20 +103,22 @@ func GetRequestID(ctx Context) string { // WithResponseWriter returns a new context and response writer that makes // interesting response statistics available within the context. func WithResponseWriter(ctx Context, w http.ResponseWriter) (Context, http.ResponseWriter) { - irw := instrumentedResponseWriter{ - ResponseWriter: w, - Context: ctx, - } - if closeNotifier, ok := w.(http.CloseNotifier); ok { irwCN := &instrumentedResponseWriterCN{ - instrumentedResponseWriter: irw, - CloseNotifier: closeNotifier, + instrumentedResponseWriter: instrumentedResponseWriter{ + ResponseWriter: w, + Context: ctx, + }, + CloseNotifier: closeNotifier, } return irwCN, irwCN } + irw := instrumentedResponseWriter{ + ResponseWriter: w, + Context: ctx, + } return &irw, &irw } diff --git a/src/vendor/github.com/docker/distribution/manifest/schema1/config_builder.go b/src/vendor/github.com/docker/distribution/manifest/schema1/config_builder.go index 5cdd76796..be0123731 100644 --- a/src/vendor/github.com/docker/distribution/manifest/schema1/config_builder.go +++ b/src/vendor/github.com/docker/distribution/manifest/schema1/config_builder.go @@ -9,11 +9,10 @@ import ( "github.com/docker/distribution" "github.com/docker/distribution/context" - "github.com/docker/distribution/reference" - "github.com/docker/libtrust" - "github.com/docker/distribution/digest" "github.com/docker/distribution/manifest" + "github.com/docker/distribution/reference" + "github.com/docker/libtrust" ) type diffID digest.Digest @@ -95,7 +94,7 @@ func (mb *configManifestBuilder) Build(ctx context.Context) (m distribution.Mani } if len(img.RootFS.DiffIDs) != len(mb.descriptors) { - return nil, errors.New("number of descriptors and number of layers in rootfs must match") + return nil, fmt.Errorf("number of descriptors and number of layers in rootfs must match: len(%v) != len(%v)", img.RootFS.DiffIDs, mb.descriptors) } // Generate IDs for each layer diff --git a/src/vendor/github.com/docker/distribution/manifest/schema2/manifest.go b/src/vendor/github.com/docker/distribution/manifest/schema2/manifest.go index 355b5ad4e..741998d04 100644 --- a/src/vendor/github.com/docker/distribution/manifest/schema2/manifest.go +++ b/src/vendor/github.com/docker/distribution/manifest/schema2/manifest.go @@ -17,6 +17,9 @@ const ( // MediaTypeConfig specifies the mediaType for the image configuration. MediaTypeConfig = "application/vnd.docker.container.image.v1+json" + // MediaTypePluginConfig specifies the mediaType for plugin configuration. + MediaTypePluginConfig = "application/vnd.docker.plugin.v1+json" + // MediaTypeLayer is the mediaType used for layers referenced by the // manifest. MediaTypeLayer = "application/vnd.docker.image.rootfs.diff.tar.gzip" @@ -66,7 +69,10 @@ type Manifest struct { // References returnes the descriptors of this manifests references. func (m Manifest) References() []distribution.Descriptor { - return m.Layers + references := make([]distribution.Descriptor, 0, 1+len(m.Layers)) + references = append(references, m.Config) + references = append(references, m.Layers...) + return references } // Target returns the target of this signed manifest. diff --git a/src/vendor/github.com/docker/distribution/manifest/versioned.go b/src/vendor/github.com/docker/distribution/manifest/versioned.go index c57398bde..caa6b14e8 100644 --- a/src/vendor/github.com/docker/distribution/manifest/versioned.go +++ b/src/vendor/github.com/docker/distribution/manifest/versioned.go @@ -1,8 +1,8 @@ package manifest -// Versioned provides a struct with the manifest schemaVersion and . Incoming -// content with unknown schema version can be decoded against this struct to -// check the version. +// Versioned provides a struct with the manifest schemaVersion and mediaType. +// Incoming content with unknown schema version can be decoded against this +// struct to check the version. type Versioned struct { // SchemaVersion is the image manifest schema that this image follows SchemaVersion int `json:"schemaVersion"` diff --git a/src/vendor/github.com/docker/distribution/manifests.go b/src/vendor/github.com/docker/distribution/manifests.go index 2ac7c8f21..c4fb63450 100644 --- a/src/vendor/github.com/docker/distribution/manifests.go +++ b/src/vendor/github.com/docker/distribution/manifests.go @@ -12,8 +12,13 @@ import ( // references and an optional target type Manifest interface { // References returns a list of objects which make up this manifest. - // The references are strictly ordered from base to head. A reference - // is anything which can be represented by a distribution.Descriptor + // A reference is anything which can be represented by a + // distribution.Descriptor. These can consist of layers, resources or other + // manifests. + // + // While no particular order is required, implementations should return + // them from highest to lowest priority. For example, one might want to + // return the base layer before the top layer. References() []Descriptor // Payload provides the serialized format of the manifest, in addition to @@ -36,6 +41,9 @@ type ManifestBuilder interface { // AppendReference includes the given object in the manifest after any // existing dependencies. If the add fails, such as when adding an // unsupported dependency, an error may be returned. + // + // The destination of the reference is dependent on the manifest type and + // the dependency type. AppendReference(dependency Describable) error } diff --git a/src/vendor/github.com/docker/distribution/reference/reference.go b/src/vendor/github.com/docker/distribution/reference/reference.go index bb09fa25d..02786628e 100644 --- a/src/vendor/github.com/docker/distribution/reference/reference.go +++ b/src/vendor/github.com/docker/distribution/reference/reference.go @@ -24,6 +24,8 @@ package reference import ( "errors" "fmt" + "path" + "strings" "github.com/docker/distribution/digest" ) @@ -43,6 +45,9 @@ var ( // ErrDigestInvalidFormat represents an error while trying to parse a string as a tag. ErrDigestInvalidFormat = errors.New("invalid digest format") + // ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters. + ErrNameContainsUppercase = errors.New("repository name must be lowercase") + // ErrNameEmpty is returned for empty, invalid repository names. ErrNameEmpty = errors.New("repository name must have at least one component") @@ -134,7 +139,7 @@ type Canonical interface { func SplitHostname(named Named) (string, string) { name := named.Name() match := anchoredNameRegexp.FindStringSubmatch(name) - if match == nil || len(match) != 3 { + if len(match) != 3 { return "", name } return match[1], match[2] @@ -149,7 +154,9 @@ func Parse(s string) (Reference, error) { if s == "" { return nil, ErrNameEmpty } - // TODO(dmcgowan): Provide more specific and helpful error + if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil { + return nil, ErrNameContainsUppercase + } return nil, ErrReferenceInvalidFormat } @@ -212,6 +219,13 @@ func WithTag(name Named, tag string) (NamedTagged, error) { if !anchoredTagRegexp.MatchString(tag) { return nil, ErrTagInvalidFormat } + if canonical, ok := name.(Canonical); ok { + return reference{ + name: name.Name(), + tag: tag, + digest: canonical.Digest(), + }, nil + } return taggedReference{ name: name.Name(), tag: tag, @@ -224,12 +238,34 @@ func WithDigest(name Named, digest digest.Digest) (Canonical, error) { if !anchoredDigestRegexp.MatchString(digest.String()) { return nil, ErrDigestInvalidFormat } + if tagged, ok := name.(Tagged); ok { + return reference{ + name: name.Name(), + tag: tagged.Tag(), + digest: digest, + }, nil + } return canonicalReference{ name: name.Name(), digest: digest, }, nil } +// Match reports whether ref matches the specified pattern. +// See https://godoc.org/path#Match for supported patterns. +func Match(pattern string, ref Reference) (bool, error) { + matched, err := path.Match(pattern, ref.String()) + if namedRef, isNamed := ref.(Named); isNamed && !matched { + matched, _ = path.Match(pattern, namedRef.Name()) + } + return matched, err +} + +// TrimNamed removes any tag or digest from the named reference. +func TrimNamed(ref Named) Named { + return repository(ref.Name()) +} + func getBestReferenceType(ref reference) Reference { if ref.name == "" { // Allow digest only references diff --git a/src/vendor/github.com/docker/distribution/registry/api/v2/headerparser.go b/src/vendor/github.com/docker/distribution/registry/api/v2/headerparser.go new file mode 100644 index 000000000..9bc41a3a6 --- /dev/null +++ b/src/vendor/github.com/docker/distribution/registry/api/v2/headerparser.go @@ -0,0 +1,161 @@ +package v2 + +import ( + "fmt" + "regexp" + "strings" + "unicode" +) + +var ( + // according to rfc7230 + reToken = regexp.MustCompile(`^[^"(),/:;<=>?@[\]{}[:space:][:cntrl:]]+`) + reQuotedValue = regexp.MustCompile(`^[^\\"]+`) + reEscapedCharacter = regexp.MustCompile(`^[[:blank:][:graph:]]`) +) + +// parseForwardedHeader is a benevolent parser of Forwarded header defined in rfc7239. The header contains +// a comma-separated list of forwarding key-value pairs. Each list element is set by single proxy. The +// function parses only the first element of the list, which is set by the very first proxy. It returns a map +// of corresponding key-value pairs and an unparsed slice of the input string. +// +// Examples of Forwarded header values: +// +// 1. Forwarded: For=192.0.2.43; Proto=https,For="[2001:db8:cafe::17]",For=unknown +// 2. Forwarded: for="192.0.2.43:443"; host="registry.example.org", for="10.10.05.40:80" +// +// The first will be parsed into {"for": "192.0.2.43", "proto": "https"} while the second into +// {"for": "192.0.2.43:443", "host": "registry.example.org"}. +func parseForwardedHeader(forwarded string) (map[string]string, string, error) { + // Following are states of forwarded header parser. Any state could transition to a failure. + const ( + // terminating state; can transition to Parameter + stateElement = iota + // terminating state; can transition to KeyValueDelimiter + stateParameter + // can transition to Value + stateKeyValueDelimiter + // can transition to one of { QuotedValue, PairEnd } + stateValue + // can transition to one of { EscapedCharacter, PairEnd } + stateQuotedValue + // can transition to one of { QuotedValue } + stateEscapedCharacter + // terminating state; can transition to one of { Parameter, Element } + statePairEnd + ) + + var ( + parameter string + value string + parse = forwarded[:] + res = map[string]string{} + state = stateElement + ) + +Loop: + for { + // skip spaces unless in quoted value + if state != stateQuotedValue && state != stateEscapedCharacter { + parse = strings.TrimLeftFunc(parse, unicode.IsSpace) + } + + if len(parse) == 0 { + if state != stateElement && state != statePairEnd && state != stateParameter { + return nil, parse, fmt.Errorf("unexpected end of input") + } + // terminating + break + } + + switch state { + // terminate at list element delimiter + case stateElement: + if parse[0] == ',' { + parse = parse[1:] + break Loop + } + state = stateParameter + + // parse parameter (the key of key-value pair) + case stateParameter: + match := reToken.FindString(parse) + if len(match) == 0 { + return nil, parse, fmt.Errorf("failed to parse token at position %d", len(forwarded)-len(parse)) + } + parameter = strings.ToLower(match) + parse = parse[len(match):] + state = stateKeyValueDelimiter + + // parse '=' + case stateKeyValueDelimiter: + if parse[0] != '=' { + return nil, parse, fmt.Errorf("expected '=', not '%c' at position %d", parse[0], len(forwarded)-len(parse)) + } + parse = parse[1:] + state = stateValue + + // parse value or quoted value + case stateValue: + if parse[0] == '"' { + parse = parse[1:] + state = stateQuotedValue + } else { + value = reToken.FindString(parse) + if len(value) == 0 { + return nil, parse, fmt.Errorf("failed to parse value at position %d", len(forwarded)-len(parse)) + } + if _, exists := res[parameter]; exists { + return nil, parse, fmt.Errorf("duplicate parameter %q at position %d", parameter, len(forwarded)-len(parse)) + } + res[parameter] = value + parse = parse[len(value):] + value = "" + state = statePairEnd + } + + // parse a part of quoted value until the first backslash + case stateQuotedValue: + match := reQuotedValue.FindString(parse) + value += match + parse = parse[len(match):] + switch { + case len(parse) == 0: + return nil, parse, fmt.Errorf("unterminated quoted string") + case parse[0] == '"': + res[parameter] = value + value = "" + parse = parse[1:] + state = statePairEnd + case parse[0] == '\\': + parse = parse[1:] + state = stateEscapedCharacter + } + + // parse escaped character in a quoted string, ignore the backslash + // transition back to QuotedValue state + case stateEscapedCharacter: + c := reEscapedCharacter.FindString(parse) + if len(c) == 0 { + return nil, parse, fmt.Errorf("invalid escape sequence at position %d", len(forwarded)-len(parse)-1) + } + value += c + parse = parse[1:] + state = stateQuotedValue + + // expect either a new key-value pair, new list or end of input + case statePairEnd: + switch parse[0] { + case ';': + parse = parse[1:] + state = stateParameter + case ',': + state = stateElement + default: + return nil, parse, fmt.Errorf("expected ',' or ';', not %c at position %d", parse[0], len(forwarded)-len(parse)) + } + } + } + + return res, parse, nil +} diff --git a/src/vendor/github.com/docker/distribution/registry/api/v2/urls.go b/src/vendor/github.com/docker/distribution/registry/api/v2/urls.go index a959aaa89..e2e242eab 100644 --- a/src/vendor/github.com/docker/distribution/registry/api/v2/urls.go +++ b/src/vendor/github.com/docker/distribution/registry/api/v2/urls.go @@ -1,8 +1,10 @@ package v2 import ( + "net" "net/http" "net/url" + "strconv" "strings" "github.com/docker/distribution/reference" @@ -49,10 +51,14 @@ func NewURLBuilderFromRequest(r *http.Request, relative bool) *URLBuilder { var scheme string forwardedProto := r.Header.Get("X-Forwarded-Proto") + // TODO: log the error + forwardedHeader, _, _ := parseForwardedHeader(r.Header.Get("Forwarded")) switch { case len(forwardedProto) > 0: scheme = forwardedProto + case len(forwardedHeader["proto"]) > 0: + scheme = forwardedHeader["proto"] case r.TLS != nil: scheme = "https" case len(r.URL.Scheme) > 0: @@ -62,14 +68,46 @@ func NewURLBuilderFromRequest(r *http.Request, relative bool) *URLBuilder { } host := r.Host - forwardedHost := r.Header.Get("X-Forwarded-Host") - if len(forwardedHost) > 0 { + + if forwardedHost := r.Header.Get("X-Forwarded-Host"); len(forwardedHost) > 0 { // According to the Apache mod_proxy docs, X-Forwarded-Host can be a // comma-separated list of hosts, to which each proxy appends the // requested host. We want to grab the first from this comma-separated // list. hosts := strings.SplitN(forwardedHost, ",", 2) host = strings.TrimSpace(hosts[0]) + } else if addr, exists := forwardedHeader["for"]; exists { + host = addr + } else if h, exists := forwardedHeader["host"]; exists { + host = h + } + + portLessHost, port := host, "" + if !isIPv6Address(portLessHost) { + // with go 1.6, this would treat the last part of IPv6 address as a port + portLessHost, port, _ = net.SplitHostPort(host) + } + if forwardedPort := r.Header.Get("X-Forwarded-Port"); len(port) == 0 && len(forwardedPort) > 0 { + ports := strings.SplitN(forwardedPort, ",", 2) + forwardedPort = strings.TrimSpace(ports[0]) + if _, err := strconv.ParseInt(forwardedPort, 10, 32); err == nil { + port = forwardedPort + } + } + + if len(portLessHost) > 0 { + host = portLessHost + } + if len(port) > 0 { + // remove enclosing brackets of ipv6 address otherwise they will be duplicated + if len(host) > 1 && host[0] == '[' && host[len(host)-1] == ']' { + host = host[1 : len(host)-1] + } + // JoinHostPort properly encloses ipv6 addresses in square brackets + host = net.JoinHostPort(host, port) + } else if isIPv6Address(host) && host[0] != '[' { + // ipv6 needs to be enclosed in square brackets in urls + host = "[" + host + "]" } basePath := routeDescriptorsMap[RouteNameBase].Path @@ -249,3 +287,28 @@ func appendValues(u string, values ...url.Values) string { return appendValuesURL(up, values...).String() } + +// isIPv6Address returns true if given string is a valid IPv6 address. No port is allowed. The address may be +// enclosed in square brackets. +func isIPv6Address(host string) bool { + if len(host) > 1 && host[0] == '[' && host[len(host)-1] == ']' { + host = host[1 : len(host)-1] + } + // The IPv6 scoped addressing zone identifier starts after the last percent sign. + if i := strings.LastIndexByte(host, '%'); i > 0 { + host = host[:i] + } + ip := net.ParseIP(host) + if ip == nil { + return false + } + if ip.To16() == nil { + return false + } + if ip.To4() == nil { + return true + } + // dot can be present in ipv4-mapped address, it needs to come after a colon though + i := strings.IndexAny(host, ":.") + return i >= 0 && host[i] == ':' +} diff --git a/src/vendor/github.com/docker/distribution/registry/auth/auth.go b/src/vendor/github.com/docker/distribution/registry/auth/auth.go index 0cb37235b..1c9af8821 100644 --- a/src/vendor/github.com/docker/distribution/registry/auth/auth.go +++ b/src/vendor/github.com/docker/distribution/registry/auth/auth.go @@ -66,8 +66,9 @@ type UserInfo struct { // Resource describes a resource by type and name. type Resource struct { - Type string - Name string + Type string + Class string + Name string } // Access describes a specific action that is @@ -135,6 +136,39 @@ func (uic userInfoContext) Value(key interface{}) interface{} { return uic.Context.Value(key) } +// WithResources returns a context with the authorized resources. +func WithResources(ctx context.Context, resources []Resource) context.Context { + return resourceContext{ + Context: ctx, + resources: resources, + } +} + +type resourceContext struct { + context.Context + resources []Resource +} + +type resourceKey struct{} + +func (rc resourceContext) Value(key interface{}) interface{} { + if key == (resourceKey{}) { + return rc.resources + } + + return rc.Context.Value(key) +} + +// AuthorizedResources returns the list of resources which have +// been authorized for this request. +func AuthorizedResources(ctx context.Context) []Resource { + if resources, ok := ctx.Value(resourceKey{}).([]Resource); ok { + return resources + } + + return nil +} + // InitFunc is the type of an AccessController factory function and is used // to register the constructor for different AccesController backends. type InitFunc func(options map[string]interface{}) (AccessController, error) diff --git a/src/vendor/github.com/docker/distribution/registry/auth/token/accesscontroller.go b/src/vendor/github.com/docker/distribution/registry/auth/token/accesscontroller.go index 5b1ff7caa..4e8b7f1ce 100644 --- a/src/vendor/github.com/docker/distribution/registry/auth/token/accesscontroller.go +++ b/src/vendor/github.com/docker/distribution/registry/auth/token/accesscontroller.go @@ -176,12 +176,14 @@ func newAccessController(options map[string]interface{}) (auth.AccessController, var rootCerts []*x509.Certificate pemBlock, rawCertBundle := pem.Decode(rawCertBundle) for pemBlock != nil { - cert, err := x509.ParseCertificate(pemBlock.Bytes) - if err != nil { - return nil, fmt.Errorf("unable to parse token auth root certificate: %s", err) - } + if pemBlock.Type == "CERTIFICATE" { + cert, err := x509.ParseCertificate(pemBlock.Bytes) + if err != nil { + return nil, fmt.Errorf("unable to parse token auth root certificate: %s", err) + } - rootCerts = append(rootCerts, cert) + rootCerts = append(rootCerts, cert) + } pemBlock, rawCertBundle = pem.Decode(rawCertBundle) } @@ -259,6 +261,8 @@ func (ac *accessController) Authorized(ctx context.Context, accessItems ...auth. } } + ctx = auth.WithResources(ctx, token.resources()) + return auth.WithUser(ctx, auth.UserInfo{Name: token.Claims.Subject}), nil } diff --git a/src/vendor/github.com/docker/distribution/registry/auth/token/token.go b/src/vendor/github.com/docker/distribution/registry/auth/token/token.go index 2598f362a..850f5813f 100644 --- a/src/vendor/github.com/docker/distribution/registry/auth/token/token.go +++ b/src/vendor/github.com/docker/distribution/registry/auth/token/token.go @@ -20,6 +20,9 @@ const ( // TokenSeparator is the value which separates the header, claims, and // signature in the compact serialization of a JSON Web Token. TokenSeparator = "." + // Leeway is the Duration that will be added to NBF and EXP claim + // checks to account for clock skew as per https://tools.ietf.org/html/rfc7519#section-4.1.5 + Leeway = 60 * time.Second ) // Errors used by token parsing and verification. @@ -31,6 +34,7 @@ var ( // ResourceActions stores allowed actions on a named and typed resource. type ResourceActions struct { Type string `json:"type"` + Class string `json:"class,omitempty"` Name string `json:"name"` Actions []string `json:"actions"` } @@ -92,7 +96,7 @@ func NewToken(rawToken string) (*Token, error) { defer func() { if err != nil { - log.Errorf("error while unmarshalling raw token: %s", err) + log.Infof("error while unmarshalling raw token: %s", err) } }() @@ -132,39 +136,47 @@ func NewToken(rawToken string) (*Token, error) { func (t *Token) Verify(verifyOpts VerifyOptions) error { // Verify that the Issuer claim is a trusted authority. if !contains(verifyOpts.TrustedIssuers, t.Claims.Issuer) { - log.Errorf("token from untrusted issuer: %q", t.Claims.Issuer) + log.Infof("token from untrusted issuer: %q", t.Claims.Issuer) return ErrInvalidToken } // Verify that the Audience claim is allowed. if !contains(verifyOpts.AcceptedAudiences, t.Claims.Audience) { - log.Errorf("token intended for another audience: %q", t.Claims.Audience) + log.Infof("token intended for another audience: %q", t.Claims.Audience) return ErrInvalidToken } // Verify that the token is currently usable and not expired. - currentUnixTime := time.Now().Unix() - if !(t.Claims.NotBefore <= currentUnixTime && currentUnixTime <= t.Claims.Expiration) { - log.Errorf("token not to be used before %d or after %d - currently %d", t.Claims.NotBefore, t.Claims.Expiration, currentUnixTime) + currentTime := time.Now() + + ExpWithLeeway := time.Unix(t.Claims.Expiration, 0).Add(Leeway) + if currentTime.After(ExpWithLeeway) { + log.Infof("token not to be used after %s - currently %s", ExpWithLeeway, currentTime) + return ErrInvalidToken + } + + NotBeforeWithLeeway := time.Unix(t.Claims.NotBefore, 0).Add(-Leeway) + if currentTime.Before(NotBeforeWithLeeway) { + log.Infof("token not to be used before %s - currently %s", NotBeforeWithLeeway, currentTime) return ErrInvalidToken } // Verify the token signature. if len(t.Signature) == 0 { - log.Error("token has no signature") + log.Info("token has no signature") return ErrInvalidToken } // Verify that the signing key is trusted. signingKey, err := t.VerifySigningKey(verifyOpts) if err != nil { - log.Error(err) + log.Info(err) return ErrInvalidToken } // Finally, verify the signature of the token using the key which signed it. if err := signingKey.Verify(strings.NewReader(t.Raw), t.Header.SigningAlg, t.Signature); err != nil { - log.Errorf("unable to verify token signature: %s", err) + log.Infof("unable to verify token signature: %s", err) return ErrInvalidToken } @@ -338,6 +350,29 @@ func (t *Token) accessSet() accessSet { return accessSet } +func (t *Token) resources() []auth.Resource { + if t.Claims == nil { + return nil + } + + resourceSet := map[auth.Resource]struct{}{} + for _, resourceActions := range t.Claims.Access { + resource := auth.Resource{ + Type: resourceActions.Type, + Class: resourceActions.Class, + Name: resourceActions.Name, + } + resourceSet[resource] = struct{}{} + } + + resources := make([]auth.Resource, 0, len(resourceSet)) + for resource := range resourceSet { + resources = append(resources, resource) + } + + return resources +} + func (t *Token) compactRaw() string { return fmt.Sprintf("%s.%s", t.Raw, joseBase64UrlEncode(t.Signature)) } diff --git a/src/vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go b/src/vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go new file mode 100644 index 000000000..2c3ebe165 --- /dev/null +++ b/src/vendor/github.com/docker/distribution/registry/client/auth/challenge/addr.go @@ -0,0 +1,27 @@ +package challenge + +import ( + "net/url" + "strings" +) + +// FROM: https://golang.org/src/net/http/http.go +// Given a string of the form "host", "host:port", or "[ipv6::address]:port", +// return true if the string includes a port. +func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") } + +// FROM: http://golang.org/src/net/http/transport.go +var portMap = map[string]string{ + "http": "80", + "https": "443", +} + +// canonicalAddr returns url.Host but always with a ":port" suffix +// FROM: http://golang.org/src/net/http/transport.go +func canonicalAddr(url *url.URL) string { + addr := url.Host + if !hasPort(addr) { + return addr + ":" + portMap[url.Scheme] + } + return addr +} diff --git a/src/vendor/github.com/docker/distribution/registry/client/auth/authchallenge.go b/src/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go similarity index 85% rename from src/vendor/github.com/docker/distribution/registry/client/auth/authchallenge.go rename to src/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go index c8cd83bb9..c9bdfc355 100644 --- a/src/vendor/github.com/docker/distribution/registry/client/auth/authchallenge.go +++ b/src/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go @@ -1,10 +1,11 @@ -package auth +package challenge import ( "fmt" "net/http" "net/url" "strings" + "sync" ) // Challenge carries information from a WWW-Authenticate response header. @@ -17,12 +18,12 @@ type Challenge struct { Parameters map[string]string } -// ChallengeManager manages the challenges for endpoints. +// Manager manages the challenges for endpoints. // The challenges are pulled out of HTTP responses. Only // responses which expect challenges should be added to // the manager, since a non-unauthorized request will be // viewed as not requiring challenges. -type ChallengeManager interface { +type Manager interface { // GetChallenges returns the challenges for the given // endpoint URL. GetChallenges(endpoint url.URL) ([]Challenge, error) @@ -36,36 +37,52 @@ type ChallengeManager interface { AddResponse(resp *http.Response) error } -// NewSimpleChallengeManager returns an instance of -// ChallengeManger which only maps endpoints to challenges +// NewSimpleManager returns an instance of +// Manger which only maps endpoints to challenges // based on the responses which have been added the // manager. The simple manager will make no attempt to // perform requests on the endpoints or cache the responses // to a backend. -func NewSimpleChallengeManager() ChallengeManager { - return simpleChallengeManager{} +func NewSimpleManager() Manager { + return &simpleManager{ + Challanges: make(map[string][]Challenge), + } } -type simpleChallengeManager map[string][]Challenge +type simpleManager struct { + sync.RWMutex + Challanges map[string][]Challenge +} -func (m simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, error) { +func normalizeURL(endpoint *url.URL) { endpoint.Host = strings.ToLower(endpoint.Host) + endpoint.Host = canonicalAddr(endpoint) +} - challenges := m[endpoint.String()] +func (m *simpleManager) GetChallenges(endpoint url.URL) ([]Challenge, error) { + normalizeURL(&endpoint) + + m.RLock() + defer m.RUnlock() + challenges := m.Challanges[endpoint.String()] return challenges, nil } -func (m simpleChallengeManager) AddResponse(resp *http.Response) error { +func (m *simpleManager) AddResponse(resp *http.Response) error { challenges := ResponseChallenges(resp) if resp.Request == nil { return fmt.Errorf("missing request reference") } urlCopy := url.URL{ Path: resp.Request.URL.Path, - Host: strings.ToLower(resp.Request.URL.Host), + Host: resp.Request.URL.Host, Scheme: resp.Request.URL.Scheme, } - m[urlCopy.String()] = challenges + normalizeURL(&urlCopy) + + m.Lock() + defer m.Unlock() + m.Challanges[urlCopy.String()] = challenges return nil } diff --git a/src/vendor/github.com/docker/distribution/registry/client/auth/session.go b/src/vendor/github.com/docker/distribution/registry/client/auth/session.go index f3497b17a..d6d884ffd 100644 --- a/src/vendor/github.com/docker/distribution/registry/client/auth/session.go +++ b/src/vendor/github.com/docker/distribution/registry/client/auth/session.go @@ -12,6 +12,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/distribution/registry/client" + "github.com/docker/distribution/registry/client/auth/challenge" "github.com/docker/distribution/registry/client/transport" ) @@ -58,7 +59,7 @@ type CredentialStore interface { // schemes. The handlers are tried in order, the higher priority authentication // methods should be first. The challengeMap holds a list of challenges for // a given root API endpoint (for example "https://registry-1.docker.io/v2/"). -func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler) transport.RequestModifier { +func NewAuthorizer(manager challenge.Manager, handlers ...AuthenticationHandler) transport.RequestModifier { return &endpointAuthorizer{ challenges: manager, handlers: handlers, @@ -66,21 +67,25 @@ func NewAuthorizer(manager ChallengeManager, handlers ...AuthenticationHandler) } type endpointAuthorizer struct { - challenges ChallengeManager + challenges challenge.Manager handlers []AuthenticationHandler transport http.RoundTripper } func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error { - v2Root := strings.Index(req.URL.Path, "/v2/") - if v2Root == -1 { + pingPath := req.URL.Path + if v2Root := strings.Index(req.URL.Path, "/v2/"); v2Root != -1 { + pingPath = pingPath[:v2Root+4] + } else if v1Root := strings.Index(req.URL.Path, "/v1/"); v1Root != -1 { + pingPath = pingPath[:v1Root] + "/v2/" + } else { return nil } ping := url.URL{ Host: req.URL.Host, Scheme: req.URL.Scheme, - Path: req.URL.Path[:v2Root+4], + Path: pingPath, } challenges, err := ea.challenges.GetChallenges(ping) @@ -90,11 +95,11 @@ func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error { if len(challenges) > 0 { for _, handler := range ea.handlers { - for _, challenge := range challenges { - if challenge.Scheme != handler.Scheme() { + for _, c := range challenges { + if c.Scheme != handler.Scheme() { continue } - if err := handler.AuthorizeRequest(req, challenge.Parameters); err != nil { + if err := handler.AuthorizeRequest(req, c.Parameters); err != nil { return err } } @@ -142,13 +147,31 @@ type Scope interface { // to a repository. type RepositoryScope struct { Repository string + Class string Actions []string } // String returns the string representation of the repository // using the scope grammar func (rs RepositoryScope) String() string { - return fmt.Sprintf("repository:%s:%s", rs.Repository, strings.Join(rs.Actions, ",")) + repoType := "repository" + if rs.Class != "" { + repoType = fmt.Sprintf("%s(%s)", repoType, rs.Class) + } + return fmt.Sprintf("%s:%s:%s", repoType, rs.Repository, strings.Join(rs.Actions, ",")) +} + +// RegistryScope represents a token scope for access +// to resources in the registry. +type RegistryScope struct { + Name string + Actions []string +} + +// String returns the string representation of the user +// using the scope grammar +func (rs RegistryScope) String() string { + return fmt.Sprintf("registry:%s:%s", rs.Name, strings.Join(rs.Actions, ",")) } // TokenHandlerOptions is used to configure a new token handler diff --git a/src/vendor/github.com/docker/distribution/registry/client/errors.go b/src/vendor/github.com/docker/distribution/registry/client/errors.go index f73e3c230..52d49d5d2 100644 --- a/src/vendor/github.com/docker/distribution/registry/client/errors.go +++ b/src/vendor/github.com/docker/distribution/registry/client/errors.go @@ -9,6 +9,7 @@ import ( "net/http" "github.com/docker/distribution/registry/api/errcode" + "github.com/docker/distribution/registry/client/auth/challenge" ) // ErrNoErrorsInBody is returned when an HTTP response body parses to an empty @@ -82,21 +83,52 @@ func parseHTTPErrorResponse(statusCode int, r io.Reader) error { return errors } +func makeErrorList(err error) []error { + if errL, ok := err.(errcode.Errors); ok { + return []error(errL) + } + return []error{err} +} + +func mergeErrors(err1, err2 error) error { + return errcode.Errors(append(makeErrorList(err1), makeErrorList(err2)...)) +} + // HandleErrorResponse returns error parsed from HTTP response for an // unsuccessful HTTP response code (in the range 400 - 499 inclusive). An // UnexpectedHTTPStatusError returned for response code outside of expected // range. func HandleErrorResponse(resp *http.Response) error { - if resp.StatusCode == 401 { + if resp.StatusCode >= 400 && resp.StatusCode < 500 { + // Check for OAuth errors within the `WWW-Authenticate` header first + // See https://tools.ietf.org/html/rfc6750#section-3 + for _, c := range challenge.ResponseChallenges(resp) { + if c.Scheme == "bearer" { + var err errcode.Error + // codes defined at https://tools.ietf.org/html/rfc6750#section-3.1 + switch c.Parameters["error"] { + case "invalid_token": + err.Code = errcode.ErrorCodeUnauthorized + case "insufficient_scope": + err.Code = errcode.ErrorCodeDenied + default: + continue + } + if description := c.Parameters["error_description"]; description != "" { + err.Message = description + } else { + err.Message = err.Code.Message() + } + + return mergeErrors(err, parseHTTPErrorResponse(resp.StatusCode, resp.Body)) + } + } err := parseHTTPErrorResponse(resp.StatusCode, resp.Body) - if uErr, ok := err.(*UnexpectedHTTPResponseError); ok { + if uErr, ok := err.(*UnexpectedHTTPResponseError); ok && resp.StatusCode == 401 { return errcode.ErrorCodeUnauthorized.WithDetail(uErr.Response) } return err } - if resp.StatusCode >= 400 && resp.StatusCode < 500 { - return parseHTTPErrorResponse(resp.StatusCode, resp.Body) - } return &UnexpectedHTTPStatusError{Status: resp.Status} } diff --git a/src/vendor/github.com/docker/distribution/registry/client/repository.go b/src/vendor/github.com/docker/distribution/registry/client/repository.go index 973125561..1ebd0b183 100644 --- a/src/vendor/github.com/docker/distribution/registry/client/repository.go +++ b/src/vendor/github.com/docker/distribution/registry/client/repository.go @@ -301,18 +301,20 @@ func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, er return distribution.Descriptor{}, err } - req, err := http.NewRequest("HEAD", u, nil) - if err != nil { - return distribution.Descriptor{}, err + newRequest := func(method string) (*http.Response, error) { + req, err := http.NewRequest(method, u, nil) + if err != nil { + return nil, err + } + + for _, t := range distribution.ManifestMediaTypes() { + req.Header.Add("Accept", t) + } + resp, err := t.client.Do(req) + return resp, err } - for _, t := range distribution.ManifestMediaTypes() { - req.Header.Add("Accept", t) - } - - var attempts int - resp, err := t.client.Do(req) -check: + resp, err := newRequest("HEAD") if err != nil { return distribution.Descriptor{}, err } @@ -321,23 +323,20 @@ check: switch { case resp.StatusCode >= 200 && resp.StatusCode < 400: return descriptorFromResponse(resp) - case resp.StatusCode == http.StatusMethodNotAllowed: - req, err = http.NewRequest("GET", u, nil) + default: + // if the response is an error - there will be no body to decode. + // Issue a GET request: + // - for data from a server that does not handle HEAD + // - to get error details in case of a failure + resp, err = newRequest("GET") if err != nil { return distribution.Descriptor{}, err } + defer resp.Body.Close() - for _, t := range distribution.ManifestMediaTypes() { - req.Header.Add("Accept", t) + if resp.StatusCode >= 200 && resp.StatusCode < 400 { + return descriptorFromResponse(resp) } - - resp, err = t.client.Do(req) - attempts++ - if attempts > 1 { - return distribution.Descriptor{}, err - } - goto check - default: return distribution.Descriptor{}, HandleErrorResponse(resp) } } @@ -680,15 +679,6 @@ func (bs *blobs) Put(ctx context.Context, mediaType string, p []byte) (distribut return writer.Commit(ctx, desc) } -// createOptions is a collection of blob creation modifiers relevant to general -// blob storage intended to be configured by the BlobCreateOption.Apply method. -type createOptions struct { - Mount struct { - ShouldMount bool - From reference.Canonical - } -} - type optionFunc func(interface{}) error func (f optionFunc) Apply(v interface{}) error { @@ -699,7 +689,7 @@ func (f optionFunc) Apply(v interface{}) error { // mounted from the given canonical reference. func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption { return optionFunc(func(v interface{}) error { - opts, ok := v.(*createOptions) + opts, ok := v.(*distribution.CreateOptions) if !ok { return fmt.Errorf("unexpected options type: %T", v) } @@ -712,7 +702,7 @@ func WithMountFrom(ref reference.Canonical) distribution.BlobCreateOption { } func (bs *blobs) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) { - var opts createOptions + var opts distribution.CreateOptions for _, option := range options { err := option.Apply(&opts) diff --git a/src/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go b/src/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go index e1b17a03a..e5ff09d75 100644 --- a/src/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go +++ b/src/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go @@ -181,6 +181,7 @@ func (hrs *httpReadSeeker) reader() (io.Reader, error) { // context.GetLogger(hrs.context).Infof("Range: %s", req.Header.Get("Range")) } + req.Header.Add("Accept-Encoding", "identity") resp, err := hrs.client.Do(req) if err != nil { return nil, err diff --git a/src/vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go b/src/vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go index 68a68f081..cf125e187 100644 --- a/src/vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go +++ b/src/vendor/github.com/docker/distribution/registry/storage/cache/memory/memory.go @@ -77,37 +77,46 @@ type repositoryScopedInMemoryBlobDescriptorCache struct { } func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { - if rsimbdcp.repository == nil { + rsimbdcp.parent.mu.Lock() + repo := rsimbdcp.repository + rsimbdcp.parent.mu.Unlock() + + if repo == nil { return distribution.Descriptor{}, distribution.ErrBlobUnknown } - return rsimbdcp.repository.Stat(ctx, dgst) + return repo.Stat(ctx, dgst) } func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) Clear(ctx context.Context, dgst digest.Digest) error { - if rsimbdcp.repository == nil { + rsimbdcp.parent.mu.Lock() + repo := rsimbdcp.repository + rsimbdcp.parent.mu.Unlock() + + if repo == nil { return distribution.ErrBlobUnknown } - return rsimbdcp.repository.Clear(ctx, dgst) + return repo.Clear(ctx, dgst) } func (rsimbdcp *repositoryScopedInMemoryBlobDescriptorCache) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error { - if rsimbdcp.repository == nil { + rsimbdcp.parent.mu.Lock() + repo := rsimbdcp.repository + if repo == nil { // allocate map since we are setting it now. - rsimbdcp.parent.mu.Lock() var ok bool // have to read back value since we may have allocated elsewhere. - rsimbdcp.repository, ok = rsimbdcp.parent.repositories[rsimbdcp.repo] + repo, ok = rsimbdcp.parent.repositories[rsimbdcp.repo] if !ok { - rsimbdcp.repository = newMapBlobDescriptorCache() - rsimbdcp.parent.repositories[rsimbdcp.repo] = rsimbdcp.repository + repo = newMapBlobDescriptorCache() + rsimbdcp.parent.repositories[rsimbdcp.repo] = repo } - - rsimbdcp.parent.mu.Unlock() + rsimbdcp.repository = repo } + rsimbdcp.parent.mu.Unlock() - if err := rsimbdcp.repository.SetDescriptor(ctx, dgst, desc); err != nil { + if err := repo.SetDescriptor(ctx, dgst, desc); err != nil { return err } diff --git a/src/vendor/github.com/docker/go/LICENSE b/src/vendor/github.com/docker/go/LICENSE new file mode 100644 index 000000000..744875676 --- /dev/null +++ b/src/vendor/github.com/docker/go/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/vendor/github.com/docker/go/canonical/json/decode.go b/src/vendor/github.com/docker/go/canonical/json/decode.go new file mode 100644 index 000000000..72b981c53 --- /dev/null +++ b/src/vendor/github.com/docker/go/canonical/json/decode.go @@ -0,0 +1,1168 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Represents JSON data structure using native Go types: booleans, floats, +// strings, arrays, and maps. + +package json + +import ( + "bytes" + "encoding" + "encoding/base64" + "errors" + "fmt" + "reflect" + "runtime" + "strconv" + "unicode" + "unicode/utf16" + "unicode/utf8" +) + +// Unmarshal parses the JSON-encoded data and stores the result +// in the value pointed to by v. +// +// Unmarshal uses the inverse of the encodings that +// Marshal uses, allocating maps, slices, and pointers as necessary, +// with the following additional rules: +// +// To unmarshal JSON into a pointer, Unmarshal first handles the case of +// the JSON being the JSON literal null. In that case, Unmarshal sets +// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into +// the value pointed at by the pointer. If the pointer is nil, Unmarshal +// allocates a new value for it to point to. +// +// To unmarshal JSON into a struct, Unmarshal matches incoming object +// keys to the keys used by Marshal (either the struct field name or its tag), +// preferring an exact match but also accepting a case-insensitive match. +// Unmarshal will only set exported fields of the struct. +// +// To unmarshal JSON into an interface value, +// Unmarshal stores one of these in the interface value: +// +// bool, for JSON booleans +// float64, for JSON numbers +// string, for JSON strings +// []interface{}, for JSON arrays +// map[string]interface{}, for JSON objects +// nil for JSON null +// +// To unmarshal a JSON array into a slice, Unmarshal resets the slice length +// to zero and then appends each element to the slice. +// As a special case, to unmarshal an empty JSON array into a slice, +// Unmarshal replaces the slice with a new empty slice. +// +// To unmarshal a JSON array into a Go array, Unmarshal decodes +// JSON array elements into corresponding Go array elements. +// If the Go array is smaller than the JSON array, +// the additional JSON array elements are discarded. +// If the JSON array is smaller than the Go array, +// the additional Go array elements are set to zero values. +// +// To unmarshal a JSON object into a string-keyed map, Unmarshal first +// establishes a map to use, If the map is nil, Unmarshal allocates a new map. +// Otherwise Unmarshal reuses the existing map, keeping existing entries. +// Unmarshal then stores key-value pairs from the JSON object into the map. +// +// If a JSON value is not appropriate for a given target type, +// or if a JSON number overflows the target type, Unmarshal +// skips that field and completes the unmarshaling as best it can. +// If no more serious errors are encountered, Unmarshal returns +// an UnmarshalTypeError describing the earliest such error. +// +// The JSON null value unmarshals into an interface, map, pointer, or slice +// by setting that Go value to nil. Because null is often used in JSON to mean +// ``not present,'' unmarshaling a JSON null into any other Go type has no effect +// on the value and produces no error. +// +// When unmarshaling quoted strings, invalid UTF-8 or +// invalid UTF-16 surrogate pairs are not treated as an error. +// Instead, they are replaced by the Unicode replacement +// character U+FFFD. +// +func Unmarshal(data []byte, v interface{}) error { + // Check for well-formedness. + // Avoids filling out half a data structure + // before discovering a JSON syntax error. + var d decodeState + err := checkValid(data, &d.scan) + if err != nil { + return err + } + + d.init(data) + return d.unmarshal(v) +} + +// Unmarshaler is the interface implemented by objects +// that can unmarshal a JSON description of themselves. +// The input can be assumed to be a valid encoding of +// a JSON value. UnmarshalJSON must copy the JSON data +// if it wishes to retain the data after returning. +type Unmarshaler interface { + UnmarshalJSON([]byte) error +} + +// An UnmarshalTypeError describes a JSON value that was +// not appropriate for a value of a specific Go type. +type UnmarshalTypeError struct { + Value string // description of JSON value - "bool", "array", "number -5" + Type reflect.Type // type of Go value it could not be assigned to + Offset int64 // error occurred after reading Offset bytes +} + +func (e *UnmarshalTypeError) Error() string { + return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() +} + +// An UnmarshalFieldError describes a JSON object key that +// led to an unexported (and therefore unwritable) struct field. +// (No longer used; kept for compatibility.) +type UnmarshalFieldError struct { + Key string + Type reflect.Type + Field reflect.StructField +} + +func (e *UnmarshalFieldError) Error() string { + return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() +} + +// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "json: Unmarshal(nil)" + } + + if e.Type.Kind() != reflect.Ptr { + return "json: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "json: Unmarshal(nil " + e.Type.String() + ")" +} + +func (d *decodeState) unmarshal(v interface{}) (err error) { + defer func() { + if r := recover(); r != nil { + if _, ok := r.(runtime.Error); ok { + panic(r) + } + err = r.(error) + } + }() + + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + + d.scan.reset() + // We decode rv not rv.Elem because the Unmarshaler interface + // test must be applied at the top level of the value. + d.value(rv) + return d.savedError +} + +// A Number represents a JSON number literal. +type Number string + +// String returns the literal text of the number. +func (n Number) String() string { return string(n) } + +// Float64 returns the number as a float64. +func (n Number) Float64() (float64, error) { + return strconv.ParseFloat(string(n), 64) +} + +// Int64 returns the number as an int64. +func (n Number) Int64() (int64, error) { + return strconv.ParseInt(string(n), 10, 64) +} + +// isValidNumber reports whether s is a valid JSON number literal. +func isValidNumber(s string) bool { + // This function implements the JSON numbers grammar. + // See https://tools.ietf.org/html/rfc7159#section-6 + // and http://json.org/number.gif + + if s == "" { + return false + } + + // Optional - + if s[0] == '-' { + s = s[1:] + if s == "" { + return false + } + } + + // Digits + switch { + default: + return false + + case s[0] == '0': + s = s[1:] + + case '1' <= s[0] && s[0] <= '9': + s = s[1:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // . followed by 1 or more digits. + if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { + s = s[2:] + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // e or E followed by an optional - or + and + // 1 or more digits. + if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { + s = s[1:] + if s[0] == '+' || s[0] == '-' { + s = s[1:] + if s == "" { + return false + } + } + for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { + s = s[1:] + } + } + + // Make sure we are at the end. + return s == "" +} + +// decodeState represents the state while decoding a JSON value. +type decodeState struct { + data []byte + off int // read offset in data + scan scanner + nextscan scanner // for calls to nextValue + savedError error + useNumber bool + canonical bool +} + +// errPhase is used for errors that should not happen unless +// there is a bug in the JSON decoder or something is editing +// the data slice while the decoder executes. +var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") + +func (d *decodeState) init(data []byte) *decodeState { + d.data = data + d.off = 0 + d.savedError = nil + return d +} + +// error aborts the decoding by panicking with err. +func (d *decodeState) error(err error) { + panic(err) +} + +// saveError saves the first err it is called with, +// for reporting at the end of the unmarshal. +func (d *decodeState) saveError(err error) { + if d.savedError == nil { + d.savedError = err + } +} + +// next cuts off and returns the next full JSON value in d.data[d.off:]. +// The next value is known to be an object or array, not a literal. +func (d *decodeState) next() []byte { + c := d.data[d.off] + item, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // Our scanner has seen the opening brace/bracket + // and thinks we're still in the middle of the object. + // invent a closing brace/bracket to get it out. + if c == '{' { + d.scan.step(&d.scan, '}') + } else { + d.scan.step(&d.scan, ']') + } + + return item +} + +// scanWhile processes bytes in d.data[d.off:] until it +// receives a scan code not equal to op. +// It updates d.off and returns the new scan code. +func (d *decodeState) scanWhile(op int) int { + var newOp int + for { + if d.off >= len(d.data) { + newOp = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 + } else { + c := d.data[d.off] + d.off++ + newOp = d.scan.step(&d.scan, c) + } + if newOp != op { + break + } + } + return newOp +} + +// value decodes a JSON value from d.data[d.off:] into the value. +// it updates d.off to point past the decoded value. +func (d *decodeState) value(v reflect.Value) { + if !v.IsValid() { + _, rest, err := nextValue(d.data[d.off:], &d.nextscan) + if err != nil { + d.error(err) + } + d.off = len(d.data) - len(rest) + + // d.scan thinks we're still at the beginning of the item. + // Feed in an empty string - the shortest, simplest value - + // so that it knows we got to the end of the value. + if d.scan.redo { + // rewind. + d.scan.redo = false + d.scan.step = stateBeginValue + } + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + + n := len(d.scan.parseState) + if n > 0 && d.scan.parseState[n-1] == parseObjectKey { + // d.scan thinks we just read an object key; finish the object + d.scan.step(&d.scan, ':') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '"') + d.scan.step(&d.scan, '}') + } + + return + } + + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(v) + + case scanBeginObject: + d.object(v) + + case scanBeginLiteral: + d.literal(v) + } +} + +type unquotedValue struct{} + +// valueQuoted is like value but decodes a +// quoted string literal or literal null into an interface value. +// If it finds anything other than a quoted string literal or null, +// valueQuoted returns unquotedValue{}. +func (d *decodeState) valueQuoted() interface{} { + switch op := d.scanWhile(scanSkipSpace); op { + default: + d.error(errPhase) + + case scanBeginArray: + d.array(reflect.Value{}) + + case scanBeginObject: + d.object(reflect.Value{}) + + case scanBeginLiteral: + switch v := d.literalInterface().(type) { + case nil, string: + return v + } + } + return unquotedValue{} +} + +// indirect walks down v allocating pointers as needed, +// until it gets to a non-pointer. +// if it encounters an Unmarshaler, indirect stops and returns that. +// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { + // If v is a named type and is addressable, + // start with its address, so that if the type has pointer methods, + // we find them. + if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { + v = v.Addr() + } + for { + // Load value from interface, but only if the result will be + // usefully addressable. + if v.Kind() == reflect.Interface && !v.IsNil() { + e := v.Elem() + if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { + v = e + continue + } + } + + if v.Kind() != reflect.Ptr { + break + } + + if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { + break + } + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + if v.Type().NumMethod() > 0 { + if u, ok := v.Interface().(Unmarshaler); ok { + return u, nil, reflect.Value{} + } + if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { + return nil, u, reflect.Value{} + } + } + v = v.Elem() + } + return nil, nil, v +} + +// array consumes an array from d.data[d.off-1:], decoding into the value v. +// the first byte of the array ('[') has been read already. +func (d *decodeState) array(v reflect.Value) { + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.off-- + d.next() + return + } + + v = pv + + // Check type of target. + switch v.Kind() { + case reflect.Interface: + if v.NumMethod() == 0 { + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + } + // Otherwise it's invalid. + fallthrough + default: + d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)}) + d.off-- + d.next() + return + case reflect.Array: + case reflect.Slice: + break + } + + i := 0 + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + // Get element of array, growing if necessary. + if v.Kind() == reflect.Slice { + // Grow slice if necessary + if i >= v.Cap() { + newcap := v.Cap() + v.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) + reflect.Copy(newv, v) + v.Set(newv) + } + if i >= v.Len() { + v.SetLen(i + 1) + } + } + + if i < v.Len() { + // Decode into element. + d.value(v.Index(i)) + } else { + // Ran out of fixed array: skip. + d.value(reflect.Value{}) + } + i++ + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + + if i < v.Len() { + if v.Kind() == reflect.Array { + // Array. Zero the rest. + z := reflect.Zero(v.Type().Elem()) + for ; i < v.Len(); i++ { + v.Index(i).Set(z) + } + } else { + v.SetLen(i) + } + } + if i == 0 && v.Kind() == reflect.Slice { + v.Set(reflect.MakeSlice(v.Type(), 0, 0)) + } +} + +var nullLiteral = []byte("null") + +// object consumes an object from d.data[d.off-1:], decoding into the value v. +// the first byte ('{') of the object has been read already. +func (d *decodeState) object(v reflect.Value) { + // Check for unmarshaler. + u, ut, pv := d.indirect(v, false) + if u != nil { + d.off-- + err := u.UnmarshalJSON(d.next()) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + v = pv + + // Decoding into nil interface? Switch to non-reflect code. + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { + v.Set(reflect.ValueOf(d.objectInterface())) + return + } + + // Check type of target: struct or map[string]T + switch v.Kind() { + case reflect.Map: + // map must have string kind + t := v.Type() + if t.Key().Kind() != reflect.String { + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + if v.IsNil() { + v.Set(reflect.MakeMap(t)) + } + case reflect.Struct: + + default: + d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)}) + d.off-- + d.next() // skip over { } in input + return + } + + var mapElem reflect.Value + + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquoteBytes(item) + if !ok { + d.error(errPhase) + } + + // Figure out field corresponding to key. + var subv reflect.Value + destring := false // whether the value is wrapped in a string to be decoded first + + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() + if !mapElem.IsValid() { + mapElem = reflect.New(elemType).Elem() + } else { + mapElem.Set(reflect.Zero(elemType)) + } + subv = mapElem + } else { + var f *field + fields := cachedTypeFields(v.Type(), false) + for i := range fields { + ff := &fields[i] + if bytes.Equal(ff.nameBytes, key) { + f = ff + break + } + if f == nil && ff.equalFold(ff.nameBytes, key) { + f = ff + } + } + if f != nil { + subv = v + destring = f.quoted + for _, i := range f.index { + if subv.Kind() == reflect.Ptr { + if subv.IsNil() { + subv.Set(reflect.New(subv.Type().Elem())) + } + subv = subv.Elem() + } + subv = subv.Field(i) + } + } + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + if destring { + switch qv := d.valueQuoted().(type) { + case nil: + d.literalStore(nullLiteral, subv, false) + case string: + d.literalStore([]byte(qv), subv, true) + default: + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) + } + } else { + d.value(subv) + } + + // Write value back to map; + // if using struct, subv points into struct already. + if v.Kind() == reflect.Map { + kv := reflect.ValueOf(key).Convert(v.Type().Key()) + v.SetMapIndex(kv, subv) + } + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } +} + +// literal consumes a literal from d.data[d.off-1:], decoding into the value v. +// The first byte of the literal has been read already +// (that's how the caller knows it's a literal). +func (d *decodeState) literal(v reflect.Value) { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + + d.literalStore(d.data[start:d.off], v, false) +} + +// convertNumber converts the number literal s to a float64 or a Number +// depending on the setting of d.useNumber. +func (d *decodeState) convertNumber(s string) (interface{}, error) { + if d.useNumber { + return Number(s), nil + } + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)} + } + return f, nil +} + +var numberType = reflect.TypeOf(Number("")) + +// literalStore decodes a literal stored in item into v. +// +// fromQuoted indicates whether this literal came from unwrapping a +// string from the ",string" struct tag option. this is used only to +// produce more helpful error messages. +func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { + // Check for unmarshaler. + if len(item) == 0 { + //Empty string given + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + return + } + wantptr := item[0] == 'n' // null + u, ut, pv := d.indirect(v, wantptr) + if u != nil { + err := u.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + if item[0] != '"' { + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + } + return + } + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + err := ut.UnmarshalText(s) + if err != nil { + d.error(err) + } + return + } + + v = pv + + switch c := item[0]; c { + case 'n': // null + switch v.Kind() { + case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + v.Set(reflect.Zero(v.Type())) + // otherwise, ignore null for primitives/string + } + case 't', 'f': // true, false + value := c == 't' + switch v.Kind() { + default: + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + } + case reflect.Bool: + v.SetBool(value) + case reflect.Interface: + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(value)) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)}) + } + } + + case '"': // string + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + switch v.Kind() { + default: + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + case reflect.Slice: + if v.Type().Elem().Kind() != reflect.Uint8 { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + break + } + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + d.saveError(err) + break + } + v.SetBytes(b[:n]) + case reflect.String: + v.SetString(string(s)) + case reflect.Interface: + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(string(s))) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)}) + } + } + + default: // number + if c != '-' && (c < '0' || c > '9') { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + s := string(item) + switch v.Kind() { + default: + if v.Kind() == reflect.String && v.Type() == numberType { + v.SetString(s) + if !isValidNumber(s) { + d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) + } + break + } + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + } + case reflect.Interface: + n, err := d.convertNumber(s) + if err != nil { + d.saveError(err) + break + } + if v.NumMethod() != 0 { + d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)}) + break + } + v.Set(reflect.ValueOf(n)) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(s, 10, 64) + if err != nil || v.OverflowInt(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetInt(n) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(s, 10, 64) + if err != nil || v.OverflowUint(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetUint(n) + + case reflect.Float32, reflect.Float64: + n, err := strconv.ParseFloat(s, v.Type().Bits()) + if err != nil || v.OverflowFloat(n) { + d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)}) + break + } + v.SetFloat(n) + } + } +} + +// The xxxInterface routines build up a value to be stored +// in an empty interface. They are not strictly necessary, +// but they avoid the weight of reflection in this common case. + +// valueInterface is like value but returns interface{} +func (d *decodeState) valueInterface() interface{} { + switch d.scanWhile(scanSkipSpace) { + default: + d.error(errPhase) + panic("unreachable") + case scanBeginArray: + return d.arrayInterface() + case scanBeginObject: + return d.objectInterface() + case scanBeginLiteral: + return d.literalInterface() + } +} + +// arrayInterface is like array but returns []interface{}. +func (d *decodeState) arrayInterface() []interface{} { + var v = make([]interface{}, 0) + for { + // Look ahead for ] - can only happen on first iteration. + op := d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + + // Back up so d.value can have the byte we just read. + d.off-- + d.scan.undo(op) + + v = append(v, d.valueInterface()) + + // Next token must be , or ]. + op = d.scanWhile(scanSkipSpace) + if op == scanEndArray { + break + } + if op != scanArrayValue { + d.error(errPhase) + } + } + return v +} + +// objectInterface is like object but returns map[string]interface{}. +func (d *decodeState) objectInterface() map[string]interface{} { + m := make(map[string]interface{}) + for { + // Read opening " of string key or closing }. + op := d.scanWhile(scanSkipSpace) + if op == scanEndObject { + // closing } - can only happen on first iteration. + break + } + if op != scanBeginLiteral { + d.error(errPhase) + } + + // Read string key. + start := d.off - 1 + op = d.scanWhile(scanContinue) + item := d.data[start : d.off-1] + key, ok := unquote(item) + if !ok { + d.error(errPhase) + } + + // Read : before value. + if op == scanSkipSpace { + op = d.scanWhile(scanSkipSpace) + } + if op != scanObjectKey { + d.error(errPhase) + } + + // Read value. + m[key] = d.valueInterface() + + // Next token must be , or }. + op = d.scanWhile(scanSkipSpace) + if op == scanEndObject { + break + } + if op != scanObjectValue { + d.error(errPhase) + } + } + return m +} + +// literalInterface is like literal but returns an interface value. +func (d *decodeState) literalInterface() interface{} { + // All bytes inside literal return scanContinue op code. + start := d.off - 1 + op := d.scanWhile(scanContinue) + + // Scan read one byte too far; back up. + d.off-- + d.scan.undo(op) + item := d.data[start:d.off] + + switch c := item[0]; c { + case 'n': // null + return nil + + case 't', 'f': // true, false + return c == 't' + + case '"': // string + s, ok := unquote(item) + if !ok { + d.error(errPhase) + } + return s + + default: // number + if c != '-' && (c < '0' || c > '9') { + d.error(errPhase) + } + n, err := d.convertNumber(string(item)) + if err != nil { + d.saveError(err) + } + return n + } +} + +// getu4 decodes \uXXXX from the beginning of s, returning the hex value, +// or it returns -1. +func getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + r, err := strconv.ParseUint(string(s[2:6]), 16, 64) + if err != nil { + return -1 + } + return rune(r) +} + +// unquote converts a quoted JSON string literal s into an actual string t. +// The rules are different than for Go, so cannot use strconv.Unquote. +func unquote(s []byte) (t string, ok bool) { + s, ok = unquoteBytes(s) + t = string(s) + return +} + +func unquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + // Check for unusual characters. If there are none, + // then no unquoting is needed, so return a slice of the + // original bytes. + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + // Out of room? Can only happen if s is full of + // malformed UTF-8 and we're replacing each + // byte with RuneError. + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rr := getu4(s[r:]) + if rr < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rr) { + rr1 := getu4(s[r:]) + if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + // A valid pair; consume. + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + // Invalid surrogate; fall back to replacement rune. + rr = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rr) + } + + // Quote, control characters are invalid. + case c == '"', c < ' ': + return + + // ASCII + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + // Coerce to well-formed UTF-8. + default: + rr, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + return b[0:w], true +} diff --git a/src/vendor/github.com/docker/go/canonical/json/encode.go b/src/vendor/github.com/docker/go/canonical/json/encode.go new file mode 100644 index 000000000..f3491b160 --- /dev/null +++ b/src/vendor/github.com/docker/go/canonical/json/encode.go @@ -0,0 +1,1250 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package json implements encoding and decoding of JSON objects as defined in +// RFC 4627. The mapping between JSON objects and Go values is described +// in the documentation for the Marshal and Unmarshal functions. +// +// See "JSON and Go" for an introduction to this package: +// https://golang.org/doc/articles/json_and_go.html +package json + +import ( + "bytes" + "encoding" + "encoding/base64" + "fmt" + "math" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +// Marshal returns the JSON encoding of v. +// +// Marshal traverses the value v recursively. +// If an encountered value implements the Marshaler interface +// and is not a nil pointer, Marshal calls its MarshalJSON method +// to produce JSON. If no MarshalJSON method is present but the +// value implements encoding.TextMarshaler instead, Marshal calls +// its MarshalText method. +// The nil pointer exception is not strictly necessary +// but mimics a similar, necessary exception in the behavior of +// UnmarshalJSON. +// +// Otherwise, Marshal uses the following type-dependent default encodings: +// +// Boolean values encode as JSON booleans. +// +// Floating point, integer, and Number values encode as JSON numbers. +// +// String values encode as JSON strings coerced to valid UTF-8, +// replacing invalid bytes with the Unicode replacement rune. +// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" +// to keep some browsers from misinterpreting JSON output as HTML. +// Ampersand "&" is also escaped to "\u0026" for the same reason. +// +// Array and slice values encode as JSON arrays, except that +// []byte encodes as a base64-encoded string, and a nil slice +// encodes as the null JSON object. +// +// Struct values encode as JSON objects. Each exported struct field +// becomes a member of the object unless +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option. +// The empty values are false, 0, any +// nil pointer or interface value, and any array, slice, map, or string of +// length zero. The object's default key string is the struct field name +// but can be specified in the struct field's tag value. The "json" key in +// the struct field's tag value is the key name, followed by an optional comma +// and options. Examples: +// +// // Field is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "myName". +// Field int `json:"myName"` +// +// // Field appears in JSON as key "myName" and +// // the field is omitted from the object if its value is empty, +// // as defined above. +// Field int `json:"myName,omitempty"` +// +// // Field appears in JSON as key "Field" (the default), but +// // the field is skipped if empty. +// // Note the leading comma. +// Field int `json:",omitempty"` +// +// The "string" option signals that a field is stored as JSON inside a +// JSON-encoded string. It applies only to fields of string, floating point, +// integer, or boolean types. This extra level of encoding is sometimes used +// when communicating with JavaScript programs: +// +// Int64String int64 `json:",string"` +// +// The key name will be used if it's a non-empty string consisting of +// only Unicode letters, digits, dollar signs, percent signs, hyphens, +// underscores and slashes. +// +// Anonymous struct fields are usually marshaled as if their inner exported fields +// were fields in the outer struct, subject to the usual Go visibility rules amended +// as described in the next paragraph. +// An anonymous struct field with a name given in its JSON tag is treated as +// having that name, rather than being anonymous. +// An anonymous struct field of interface type is treated the same as having +// that type as its name, rather than being anonymous. +// +// The Go visibility rules for struct fields are amended for JSON when +// deciding which field to marshal or unmarshal. If there are +// multiple fields at the same level, and that level is the least +// nested (and would therefore be the nesting level selected by the +// usual Go rules), the following extra rules apply: +// +// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered, +// even if there are multiple untagged fields that would otherwise conflict. +// 2) If there is exactly one field (tagged or not according to the first rule), that is selected. +// 3) Otherwise there are multiple fields, and all are ignored; no error occurs. +// +// Handling of anonymous struct fields is new in Go 1.1. +// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of +// an anonymous struct field in both current and earlier versions, give the field +// a JSON tag of "-". +// +// Map values encode as JSON objects. +// The map's key type must be string; the map keys are used as JSON object +// keys, subject to the UTF-8 coercion described for string values above. +// +// Pointer values encode as the value pointed to. +// A nil pointer encodes as the null JSON object. +// +// Interface values encode as the value contained in the interface. +// A nil interface value encodes as the null JSON object. +// +// Channel, complex, and function values cannot be encoded in JSON. +// Attempting to encode such a value causes Marshal to return +// an UnsupportedTypeError. +// +// JSON cannot represent cyclic data structures and Marshal does not +// handle them. Passing cyclic structures to Marshal will result in +// an infinite recursion. +// +func Marshal(v interface{}) ([]byte, error) { + return marshal(v, false) +} + +// MarshalIndent is like Marshal but applies Indent to format the output. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + b, err := Marshal(v) + if err != nil { + return nil, err + } + var buf bytes.Buffer + err = Indent(&buf, b, prefix, indent) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// MarshalCanonical is like Marshal but encodes into Canonical JSON. +// Read more at: http://wiki.laptop.org/go/Canonical_JSON +func MarshalCanonical(v interface{}) ([]byte, error) { + return marshal(v, true) +} + +func marshal(v interface{}, canonical bool) ([]byte, error) { + e := &encodeState{canonical: canonical} + err := e.marshal(v) + if err != nil { + return nil, err + } + return e.Bytes(), nil +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 +// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 +// so that the JSON will be safe to embed inside HTML