forked from coolaj86/walnut.js
		
	Compare commits
	
		
			1 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 26c0ed66cf | 
| @ -1,4 +0,0 @@ | ||||
| v1.2.5 - Beginning of CHANGELOG | ||||
| 	* has semi-functional launchpad | ||||
| 	* OAuth3 with issuer-rewrite merged in | ||||
| 	* capabilities API | ||||
							
								
								
									
										55
									
								
								INSTALL.md
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								INSTALL.md
									
									
									
									
									
								
							| @ -19,19 +19,18 @@ Pre-requisites: | ||||
| * You own a domain | ||||
|   * through Daplie Domains | ||||
|   * or you understand domains and DNS and all that stuff | ||||
| * Install bower `npm install -g bower` | ||||
| 
 | ||||
| Choose a domain | ||||
| --------------- | ||||
| 
 | ||||
| For the purpose of this instruction we'll assume that your domain is `foo.com`, | ||||
| For the purpose of this instruction we'll assume that your domain is `example.com`, | ||||
| but you can use, say, `johndoe.daplie.me` for testing through Daplie Domains. | ||||
| 
 | ||||
| Anyway, go ahead and set the bash variable `$my_domain` for the purposes of the | ||||
| rest of this tutorial: | ||||
| 
 | ||||
| ``` | ||||
| my_domain=foo.com | ||||
| my_domain=example.com | ||||
| ``` | ||||
| 
 | ||||
| You can purchase a domain with daplie tools | ||||
| @ -48,17 +47,17 @@ Subdomains | ||||
| Auth will be loaded with the following domains | ||||
| 
 | ||||
| ``` | ||||
| provider.foo.com | ||||
| api.provider.foo.com | ||||
| provider.example.com | ||||
| api.provider.example.com | ||||
| ``` | ||||
| 
 | ||||
| The Hello World app will be loaded with the following domains | ||||
| 
 | ||||
| ``` | ||||
| foo.com | ||||
| www.foo.com | ||||
| api.foo.com | ||||
| assets.foo.com | ||||
| example.com | ||||
| www.example.com | ||||
| api.example.com | ||||
| assets.example.com | ||||
| ``` | ||||
| 
 | ||||
| The domains can be setup through the Daplie Desktop App or with daplie-tools | ||||
| @ -70,9 +69,6 @@ Replace `foodevice` with whatever you like to call this device | ||||
| my_device=foodevice | ||||
| 
 | ||||
| # curl https://api.oauth3.org/api/tunnel@oauth3.org/checkip | ||||
| # READ THIS: localhost is being used as an example. | ||||
| # Your IP address should be public facing (i.e. port-forwarding is enabled on your router). | ||||
| # If it isn't, then you need something like goldilocks providing a tunnel. | ||||
| my_address=127.0.0.1 | ||||
| 
 | ||||
| # set device address and attach primary domain | ||||
| @ -96,7 +92,7 @@ Walnut must sit behind a proxy that properly terminates https and sets the `X-Fo | ||||
| Goldilocks can do this, as well as manage daplie domains, tunneling, etc. | ||||
| 
 | ||||
| ```bash | ||||
| curl https://git.daplie.com/Daplie/daplie-snippets/raw/master/install.sh | bash | ||||
| curl https://daplie.me/install-scripts | bash | ||||
| 
 | ||||
| daplie-install-goldilocks | ||||
| ``` | ||||
| @ -105,16 +101,16 @@ daplie-install-goldilocks | ||||
| Example `/etc/goldilocks/goldilocks.yml`: | ||||
| ```yml | ||||
| tls: | ||||
|   email: user@mailservice.com | ||||
|   email: domains@example.com | ||||
|   servernames: | ||||
|     - foo.com | ||||
|     - www.foo.com | ||||
|     - api.foo.com | ||||
|     - assets.foo.com | ||||
|     - cloud.foo.com | ||||
|     - api.cloud.foo.com | ||||
|     - provider.foo.com | ||||
|     - api.provider.foo.com | ||||
|     - example.com | ||||
|     - www.example.com | ||||
|     - api.example.com | ||||
|     - assets.example.com | ||||
|     - cloud.example.com | ||||
|     - api.cloud.example.com | ||||
|     - provider.example.com | ||||
|     - api.provider.example.com | ||||
| 
 | ||||
| http: | ||||
|   trust_proxy: true | ||||
| @ -129,7 +125,7 @@ Basic Walnut Install | ||||
| -------------------- | ||||
| 
 | ||||
| ```bash | ||||
| curl https://git.daplie.com/Daplie/daplie-snippets/raw/master/install.sh | bash | ||||
| curl https://daplie.me/install-scripts | bash | ||||
| 
 | ||||
| daplie-install-walnut | ||||
| ``` | ||||
| @ -166,8 +162,8 @@ Resetting the Initialization | ||||
| Once you run the app the initialization files will appear in these locations | ||||
| 
 | ||||
| ``` | ||||
| /srv/walnut/var/walnut+config@daplie.com.sqlite3 | ||||
| /srv/walnut/config/foo.com.json | ||||
| /srv/walnut/var/com.daplie.walnut.config.sqlite3 | ||||
| /srv/walnut/config/<domain.tld>/config.json | ||||
| ``` | ||||
| 
 | ||||
| Deleting those files and restarting walnut will reset it to its bootstrap state. | ||||
| @ -192,7 +188,7 @@ For the APIs for that we'll install the `issuer@oauth3.org` API package and enab | ||||
| 
 | ||||
| ```bash | ||||
| # API packaged for walnut | ||||
| git clone https://git.daplie.com/OAuth3/issuer_oauth3.org.git /srv/walnut/packages/rest/issuer@oauth3.org | ||||
| git clone https://git.daplie.com/OAuth3/org.oauth3.provider.git /srv/walnut/packages/rest/issuer@oauth3.org | ||||
| pushd /srv/walnut/packages/rest/issuer@oauth3.org/ | ||||
|     git checkout v1.2 | ||||
|     npm install | ||||
| @ -228,13 +224,13 @@ It is intended to provide a way to use various mail services in the future, | ||||
| just bear with us for the time being (or open a Merge Request). | ||||
| 
 | ||||
| ```bash | ||||
| mkdir -p /srv/walnut/var/provider.$my_domain | ||||
| vim /srv/walnut/var/provider.$my_domain/config.json | ||||
| vim /srv/walnut/var/$my_domain/config.json | ||||
| ``` | ||||
| 
 | ||||
| ```json | ||||
| { "mailgun.org": { | ||||
|     "apiKey": "key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||||
|   , "apiPublicKey": "pubkey-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||||
|   , "auth": { | ||||
|       "user": "robtherobot@example.com" | ||||
|     , "pass": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||||
| @ -270,9 +266,8 @@ What it should look like: | ||||
|   models.js | ||||
|   rest.js | ||||
| 
 | ||||
| /srv/walnut/packages/client-api-grants/provider.foo.com | ||||
| /srv/walnut/packages/client-api-grants/example.com | ||||
|   ''' | ||||
|   issuer@oauth3.org | ||||
|   hello@example.com | ||||
|   ''' | ||||
| ``` | ||||
|  | ||||
							
								
								
									
										42
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								LICENSE
									
									
									
									
									
								
							| @ -1,41 +1,3 @@ | ||||
| Copyright 2017 Daplie, Inc | ||||
| Copyright 2017 Daplie Inc. | ||||
| 
 | ||||
| This is open source software; you can redistribute it and/or modify it under the | ||||
| terms of either: | ||||
| 
 | ||||
|    a) the "MIT License" | ||||
|    b) the "Apache-2.0 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. | ||||
| 
 | ||||
| Apache-2.0 License Summary | ||||
| 
 | ||||
|    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. | ||||
| All Rights Reserved | ||||
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
								
							| @ -53,31 +53,8 @@ Installation | ||||
| 
 | ||||
| We're still in a stage where the installation generally requires many manual steps. | ||||
| 
 | ||||
| ```bash | ||||
| curl https://git.daplie.com/Daplie/walnut.js/raw/v1.2/installer/get.sh | bash | ||||
| ``` | ||||
| 
 | ||||
| See [INSTALL.md](/INSTALL.md) | ||||
| 
 | ||||
| ### Uninstall | ||||
| 
 | ||||
| ```bash | ||||
| rm -rf /srv/walnut/ /var/walnut/ /etc/walnut/ /opt/walnut/ /var/log/walnut/ /etc/systemd/system/walnut.service /etc/tmpfiles.d/walnut.conf | ||||
| ``` | ||||
| 
 | ||||
| Usage | ||||
| ----- | ||||
| 
 | ||||
| Here's how you run the thing, once installed: | ||||
| 
 | ||||
| ``` | ||||
| /opt/walnut/bin/node /srv/walnut/core/bin/walnut.js | ||||
| ``` | ||||
| 
 | ||||
| It listens on all addresses, port 3000. | ||||
| 
 | ||||
| TODO: Add config to restrict listening to localhost. | ||||
| 
 | ||||
| API | ||||
| --- | ||||
| 
 | ||||
|  | ||||
| @ -149,10 +149,9 @@ module.exports.create = function () { | ||||
|   process.on('unhandledRejection', function (err) { | ||||
|     // this should always throw
 | ||||
|     // (it means somewhere we're not using bluebird by accident)
 | ||||
|     console.error('[caught unhandledRejection]:', err.message || ''); | ||||
|     Object.keys(err).forEach(function (key) { | ||||
|       console.log('\t'+key+': '+err[key]); | ||||
|     }); | ||||
|     console.error('[caught] [unhandledRejection]'); | ||||
|     console.error(Object.keys(err)); | ||||
|     console.error(err); | ||||
|     console.error(err.stack); | ||||
|   }); | ||||
|   process.on('rejectionHandled', function (msg) { | ||||
|  | ||||
							
								
								
									
										10
									
								
								dist/etc/systemd/system/walnut.service
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								dist/etc/systemd/system/walnut.service
									
									
									
									
										vendored
									
									
								
							| @ -19,15 +19,15 @@ StartLimitBurst=3 | ||||
| 
 | ||||
| # User and group the process will run as | ||||
| # (www-data is the de facto standard on most systems) | ||||
| User=MY_USER | ||||
| Group=MY_GROUP | ||||
| User=www-data | ||||
| Group=www-data | ||||
| 
 | ||||
| # If we need to pass environment variables in the future | ||||
| ; Environment=GOLDILOCKS_PATH=/opt/walnut | ||||
| 
 | ||||
| # Set a sane working directory, sane flags, and specify how to reload the config file | ||||
| WorkingDirectory=/opt/walnut | ||||
| ExecStart=/opt/walnut/bin/node /opt/walnut/core/bin/walnut.js --config=/etc/walnut/walnut.yml | ||||
| WorkingDirectory=/srv/www | ||||
| ExecStart=/usr/local/bin/node /srv/walnut/core/bin/walnut.js --config=/etc/walnut/walnut.yml | ||||
| ExecReload=/bin/kill -USR1 $MAINPID | ||||
| 
 | ||||
| # Limit the number of file descriptors and processes; see `man systemd.exec` for more limit settings. | ||||
| @ -46,7 +46,7 @@ ProtectSystem=full | ||||
| # … except TLS/SSL, ACME, and Let's Encrypt certificates | ||||
| #   and /var/log/, because we want a place where logs can go. | ||||
| #   This merely retains r/w access rights, it does not add any new. Must still be writable on the host! | ||||
| ReadWriteDirectories=/etc/walnut /var/log/walnut /var/walnut /opt/walnut /srv/walnut | ||||
| ReadWriteDirectories=/etc/walnut /var/log/walnut /var/walnut /opt/walnut /srv/www | ||||
| 
 | ||||
| # Note: in v231 and above ReadWritePaths has been renamed to ReadWriteDirectories | ||||
| ; ReadWritePaths=/etc/walnut /var/log/walnut | ||||
|  | ||||
							
								
								
									
										11
									
								
								dist/etc/tmpfiles.d/walnut.conf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								dist/etc/tmpfiles.d/walnut.conf
									
									
									
									
										vendored
									
									
								
							| @ -1,5 +1,12 @@ | ||||
| # /etc/tmpfiles.d/goldilocks.conf | ||||
| # /etc/tmpfiles.d/walnut.conf | ||||
| # See https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html | ||||
| 
 | ||||
| # Type Path           Mode UID      GID      Age Argument | ||||
| d /run/goldilocks          0755 MY_USER MY_GROUP -   - | ||||
| d /etc/walnut          0755 www-data www-data -   - | ||||
| d /etc/ssl/walnut      0750 www-data www-data -   - | ||||
| d /srv/walnut          0775 www-data www-data -   - | ||||
| d /srv/www             0775 www-data www-data -   - | ||||
| d /opt/walnut          0775 www-data www-data -   - | ||||
| d /var/walnut          0775 www-data www-data -   - | ||||
| d /var/log/walnut      0750 www-data www-data -   - | ||||
| #d /run/walnut          0755 www-data www-data -   - | ||||
|  | ||||
							
								
								
									
										0
									
								
								dist/etc/walnut/walnut.example.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										0
									
								
								dist/etc/walnut/walnut.example.yml
									
									
									
									
										vendored
									
									
								
							
							
								
								
									
										298
									
								
								install-helper.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										298
									
								
								install-helper.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,298 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| set -e | ||||
| set -u | ||||
| 
 | ||||
| # something or other about android and tmux using PREFIX | ||||
| #: "${PREFIX:=''}" | ||||
| MY_ROOT="" | ||||
| if [ -z "${PREFIX-}" ]; then | ||||
|   MY_ROOT="" | ||||
| else | ||||
|   MY_ROOT="$PREFIX" | ||||
| fi | ||||
| # Not every platform has or needs sudo, gotta save them O(1)s... | ||||
| sudo_cmd="" | ||||
| ((EUID)) && [[ -z "${ANDROID_ROOT-}" ]] && sudo_cmd="sudo" | ||||
| 
 | ||||
| ############################### | ||||
| #                             # | ||||
| #         http_get            # | ||||
| # boilerplate for curl / wget # | ||||
| #                             # | ||||
| ############################### | ||||
| 
 | ||||
| # See https://git.daplie.com/Daplie/daplie-snippets/blob/master/bash/http-get.sh | ||||
| 
 | ||||
| http_curl_opts="-fsSL" | ||||
| http_wget_opts="--quiet" | ||||
| 
 | ||||
| http_bin="" | ||||
| http_opts="" | ||||
| http_out="" | ||||
| 
 | ||||
| detect_http_bin() | ||||
| { | ||||
|   if type -p curl >/dev/null 2>&1; then | ||||
|     http_bin="curl" | ||||
|     http_opts="$http_curl_opts" | ||||
|     http_out="-o" | ||||
|     #curl -fsSL "$url" -o "$PREFIX/tmp/$pkg" | ||||
|   elif type -p wget >/dev/null 2>&1; then | ||||
|     http_bin="wget" | ||||
|     http_opts="$http_wget_opts" | ||||
|     http_out="-O" | ||||
|     #wget --quiet "$url" -O "$PREFIX/tmp/$pkg" | ||||
|   else | ||||
|     echo "Aborted, could not find curl or wget" | ||||
|     return 7 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| http_get() | ||||
| { | ||||
|   if [ -e "$1" ]; then | ||||
|     rsync -a "$1" "$2" | ||||
|   elif type -p curl >/dev/null 2>&1; then | ||||
|     $http_bin $http_curl_opts $http_out "$2" "$1" | ||||
|   elif type -p wget >/dev/null 2>&1; then | ||||
|     $http_bin $http_wget_opts $http_out "$2" "$1" | ||||
|   else | ||||
|     echo "Aborted, could not find curl or wget" | ||||
|     return 7 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| dap_dl() | ||||
| { | ||||
|   http_get "$1" "$2" | ||||
| } | ||||
| 
 | ||||
| dap_dl_bash() | ||||
| { | ||||
|   dap_url=$1 | ||||
|   #dap_args=$2 | ||||
|   rm -rf /tmp/dap-tmp-runner.sh | ||||
|   $http_bin $http_opts $http_out /tmp/dap-tmp-runner.sh "$dap_url"; bash /tmp/dap-tmp-runner.sh; rm /tmp/dap-tmp-runner.sh | ||||
| } | ||||
| 
 | ||||
| detect_http_bin | ||||
| 
 | ||||
| ## END HTTP_GET ## | ||||
| 
 | ||||
| 
 | ||||
| mvdir_backward_compat() | ||||
| { | ||||
|   old_dir=$1 | ||||
|   new_dir=$2 | ||||
|   # The symlink has already been set up, so no need to do anything. | ||||
|   if [ -L $old_dir ] && [ $(readlink $old_dir) == "$new_dir" ]; then | ||||
|     return 0 | ||||
|   fi | ||||
| 
 | ||||
|   if [ -d $old_dir ]; then | ||||
|     if [ $(ls $old_dir | wc -l) -gt 0 ]; then | ||||
|       mv ${old_dir}/* ${new_dir}/ | ||||
|     fi | ||||
|     rm -r ${old_dir} | ||||
|     #rmdir ${old_dir} | ||||
|   fi | ||||
| 
 | ||||
|   ln -snf $new_dir $old_dir | ||||
| } | ||||
| 
 | ||||
| ################### | ||||
| #                 # | ||||
| # Install service # | ||||
| #                 # | ||||
| ################### | ||||
| 
 | ||||
| install_for_systemd() | ||||
| { | ||||
|   echo "" | ||||
|   echo "Installing as systemd service" | ||||
|   echo "" | ||||
|   mkdir -p $(dirname "$my_app_dir/$my_app_systemd_service") | ||||
|   dap_dl "$installer_base/$my_app_systemd_service" "$my_app_dir/$my_app_systemd_service" | ||||
|   $sudo_cmd mv "$my_app_dir/$my_app_systemd_service" "$MY_ROOT/$my_app_systemd_service" | ||||
|   $sudo_cmd chown -R root:root "$MY_ROOT/$my_app_systemd_service" | ||||
|   $sudo_cmd chmod 644 "$MY_ROOT/$my_app_systemd_service" | ||||
| 
 | ||||
|   mkdir -p $(dirname "$my_app_dir/$my_app_systemd_tmpfiles") | ||||
|   dap_dl "$installer_base/$my_app_systemd_tmpfiles" "$my_app_dir/$my_app_systemd_tmpfiles" | ||||
|   $sudo_cmd mv "$my_app_dir/$my_app_systemd_tmpfiles" "$MY_ROOT/$my_app_systemd_tmpfiles" | ||||
|   $sudo_cmd chown -R root:root "$MY_ROOT/$my_app_systemd_tmpfiles" | ||||
|   $sudo_cmd chmod 644 "$MY_ROOT/$my_app_systemd_tmpfiles" | ||||
| 
 | ||||
|   $sudo_cmd systemctl stop "${my_app_name}.service" >/dev/null 2>/dev/null | ||||
|   $sudo_cmd systemctl daemon-reload | ||||
|   $sudo_cmd systemctl start "${my_app_name}.service" | ||||
|   $sudo_cmd systemctl enable "${my_app_name}.service" | ||||
| 
 | ||||
|   echo "$my_app_name started with systemctl, check its status like so" | ||||
|   echo "  $sudo_cmd systemctl status $my_app_name" | ||||
|   echo "  $sudo_cmd journalctl -xe -u $my_app_name" | ||||
| } | ||||
| 
 | ||||
| install_for_launchd() | ||||
| { | ||||
|   echo "" | ||||
|   echo "Installing as launchd service" | ||||
|   echo "" | ||||
|   # See http://www.launchd.info/ | ||||
|   mkdir -p $(dirname "$my_app_dir/$my_app_launchd_service") | ||||
|   dap_dl "$installer_base/$my_app_launchd_service" "$my_app_dir/$my_app_launchd_service" | ||||
|   $sudo_cmd mv "$my_app_dir/$my_app_launchd_service" "$MY_ROOT/$my_app_launchd_service" | ||||
|   $sudo_cmd chown root:wheel "$MY_ROOT/$my_app_launchd_service" | ||||
|   $sudo_cmd chmod 0644 "$MY_ROOT/$my_app_launchd_service" | ||||
|   $sudo_cmd launchctl unload -w "$MY_ROOT/$my_app_launchd_service" >/dev/null 2>/dev/null | ||||
|   $sudo_cmd launchctl load -w "$MY_ROOT/$my_app_launchd_service" | ||||
| 
 | ||||
|   echo "$my_app_name started with launchd" | ||||
| } | ||||
| 
 | ||||
| install_etc_config() | ||||
| { | ||||
|   #echo "install etc config $MY_ROOT / $my_app_etc_config" | ||||
|   if [ ! -e "$MY_ROOT/$my_app_etc_config" ]; then | ||||
|     $sudo_cmd mkdir -p $(dirname "$MY_ROOT/$my_app_etc_config") | ||||
|     mkdir -p $(dirname "$my_app_dir/$my_app_etc_config") | ||||
|     dap_dl "$installer_base/$my_app_etc_config" "$my_app_dir/$my_app_etc_config" | ||||
|     $sudo_cmd mv "$my_app_dir/$my_app_etc_config" "$MY_ROOT/$my_app_etc_config" | ||||
|   fi | ||||
| 
 | ||||
|   $sudo_cmd chown -R www-data:www-data $(dirname "$MY_ROOT/$my_app_etc_config") || true | ||||
|   $sudo_cmd chown -R _www:_www $(dirname "$MY_ROOT/$my_app_etc_config") || true | ||||
|   $sudo_cmd chmod 775 $(dirname "$MY_ROOT/$my_app_etc_config") | ||||
|   $sudo_cmd chmod 664 "$MY_ROOT/$my_app_etc_config" | ||||
| } | ||||
| 
 | ||||
| install_service() | ||||
| { | ||||
|   install_etc_config | ||||
|   #echo "install service" | ||||
| 
 | ||||
|   installable="" | ||||
|   if [ -d "$MY_ROOT/etc/systemd/system" ]; then | ||||
|     install_for_systemd | ||||
|     installable="true" | ||||
|   fi | ||||
|   if [ -d "/Library/LaunchDaemons" ]; then | ||||
|     install_for_launchd | ||||
|     installable="true" | ||||
|   fi | ||||
|   if [ -z "$installable" ]; then | ||||
|     echo "" | ||||
|     echo "Unknown system service init type. You must install as a system service manually." | ||||
|     echo '(please file a bug with the output of "uname -a")' | ||||
|     echo "" | ||||
|   fi | ||||
|   echo "" | ||||
| } | ||||
| 
 | ||||
| ## END SERVICE_INSTALL ## | ||||
| 
 | ||||
| # Create dirs, set perms | ||||
| create_skeleton() | ||||
| { | ||||
|   $sudo_cmd mkdir -p /srv/www | ||||
|   $sudo_cmd mkdir -p /var/log/$my_app_name | ||||
|   $sudo_cmd mkdir -p /etc/$my_app_name | ||||
|   $sudo_cmd mkdir -p /var/$my_app_name | ||||
|   $sudo_cmd mkdir -p /srv/$my_app_name | ||||
|   $sudo_cmd mkdir -p /opt/$my_app_name | ||||
| } | ||||
| 
 | ||||
| # Unistall | ||||
| install_uninstaller() | ||||
| { | ||||
|   #echo "install uninstaller" | ||||
|   dap_dl "https://git.daplie.com/Daplie/walnut.js/raw/master/uninstall.sh" "./walnut-uninstall" | ||||
|   $sudo_cmd chmod 755 "./walnut-uninstall" | ||||
|   $sudo_cmd chown root:root "./walnut-uninstall" | ||||
|   $sudo_cmd mv "./walnut-uninstall" "/usr/local/bin/uninstall-walnut" | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| # Dependencies | ||||
| export NODE_PATH=/opt/walnut/lib/node_modules | ||||
| export NPM_CONFIG_PREFIX=/opt/walnut | ||||
| $sudo_cmd mkdir -p $NODE_PATH | ||||
| $sudo_cmd chown -R $(whoami) /opt/walnut | ||||
| dap_dl_bash "https://git.daplie.com/coolaj86/node-install-script/raw/master/setup-min.sh" | ||||
| 
 | ||||
| # Install | ||||
| # npm install -g 'git+https://git@git.daplie.com/Daplie/walnut.js.git#v1' | ||||
| 
 | ||||
| my_app_name=walnut | ||||
| my_app_pkg_name=com.daplie.walnut.web | ||||
| my_app_dir=$(mktemp -d) | ||||
| #installer_base="https://git.daplie.com/Daplie/walnut.js/raw/master/dist" | ||||
| #installer_base="$( dirname "${BASH_SOURCE[0]}" )/dist" | ||||
| installer_base="/srv/walnut/core/dist" | ||||
| 
 | ||||
| my_app_etc_config="etc/${my_app_name}/${my_app_name}.yml" | ||||
| my_app_systemd_service="etc/systemd/system/${my_app_name}.service" | ||||
| my_app_systemd_tmpfiles="etc/tmpfiles.d/${my_app_name}.conf" | ||||
| my_app_launchd_service="Library/LaunchDaemons/${my_app_pkg_name}.plist" | ||||
| 
 | ||||
| # Install | ||||
| install_my_app() | ||||
| { | ||||
|   # This function shouldn't need to use $sudo_cmd because it is called immediately after | ||||
|   # /srv/walnut is chown-ed and we only mess with things in that directory. | ||||
| 
 | ||||
|   #git clone git@git.daplie.com:Daplie/walnut.js.git | ||||
|   #git clone https://git.daplie.com/Daplie/walnut.js.git /srv/walnut/core | ||||
|   mkdir -p /srv/walnut/{core,lib,var,etc,config,node_modules} | ||||
|   rm -rf /srv/walnut/core/node_modules | ||||
|   ln -sf ../node_modules /srv/walnut/core/node_modules | ||||
|   mkdir -p /srv/walnut/var/sites | ||||
|   mkdir -p /srv/walnut/etc/org.oauth3.consumer | ||||
|   mkdir -p /srv/walnut/etc/org.oauth3.provider | ||||
|   mkdir -p /srv/walnut/etc/client-api-grants | ||||
|   mkdir -p /srv/walnut/packages/{rest,api,pages,services} | ||||
| 
 | ||||
|   # backwards compat | ||||
|   mvdir_backward_compat /srv/walnut/packages/client-api-grants /srv/walnut/etc/client-api-grants | ||||
|   mvdir_backward_compat /srv/walnut/packages/sites /srv/walnut/var/sites | ||||
| 
 | ||||
|   if [ ! -d "/srv/walnut/core/lib/walnut@daplie.com/setup" ]; then | ||||
|     git clone https://git.daplie.com/Daplie/walnut_launchpad.git /srv/walnut/core/lib/walnut@daplie.com/setup | ||||
|   fi | ||||
|   pushd /srv/walnut/core/lib/walnut@daplie.com/setup | ||||
|     if [ ! -d "./.git/" ]; then | ||||
|       echo "'/srv/walnut/core/lib/walnut@daplie.com/setup' exists but is not a git repository... not sure what to do here..." | ||||
|     fi | ||||
|     git checkout v1 | ||||
|     git pull | ||||
|   popd | ||||
| 
 | ||||
|   pushd /srv/walnut/core | ||||
|     /opt/walnut/bin/npm install | ||||
|   popd | ||||
| } | ||||
| 
 | ||||
| $sudo_cmd mkdir -p /srv/walnut | ||||
| $sudo_cmd chown -R $(whoami) /srv/walnut | ||||
| 
 | ||||
| install_my_app | ||||
| create_skeleton | ||||
| install_uninstaller | ||||
| install_service | ||||
| 
 | ||||
| $sudo_cmd chown -R www-data:www-data /opt/walnut || true | ||||
| $sudo_cmd chown -R _www:_www /opt/walnut || true | ||||
| $sudo_cmd chown -R www-data:www-data /srv/walnut || true | ||||
| $sudo_cmd chown -R _www:_www /srv/walnut || true | ||||
| $sudo_cmd chmod -R ug+rwX /srv/walnut | ||||
| $sudo_cmd chmod -R ug+rwX /opt/walnut | ||||
| # +s sets the setuid/setgid bit, which when set on directories makes it so anything | ||||
| # created inside the directory maintains the same user/group (depending on the bits | ||||
| # set). Any directory created within a directory with those bits set will also have | ||||
| # those bits set. When setuid or setgid bits are set on a file however it means that | ||||
| # if the file is executed it will run with the permissions of the user/group no matter | ||||
| # who actually runs it (see the ping executable for example). | ||||
| # I'm not sure that all systems actually support the use of these bits. | ||||
| find /srv/walnut -type d -exec $sudo_cmd chmod ug+s {} \; || true | ||||
| find /opt/walnut -type d -exec $sudo_cmd chmod ug+s {} \; || true | ||||
							
								
								
									
										100
									
								
								install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								install.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| set -e | ||||
| set -u | ||||
| 
 | ||||
| ############################### | ||||
| #                             # | ||||
| # boilerplate for curl / wget # | ||||
| #                             # | ||||
| ############################### | ||||
| 
 | ||||
| http_get="" | ||||
| http_opts="" | ||||
| http_out="" | ||||
| 
 | ||||
| detect_http_get() | ||||
| { | ||||
|   if type -p curl >/dev/null 2>&1; then | ||||
|     http_get="curl" | ||||
|     http_opts="-fsSL" | ||||
|     http_out="-o" | ||||
|     #curl -fsSL "$caddy_url" -o "$PREFIX/tmp/$caddy_pkg" | ||||
|   elif type -p wget >/dev/null 2>&1; then | ||||
|     http_get="wget" | ||||
|     http_opts="--quiet" | ||||
|     http_out="-O" | ||||
|     #wget --quiet "$caddy_url" -O "$PREFIX/tmp/$caddy_pkg" | ||||
|   else | ||||
|     echo "Aborted, could not find curl or wget" | ||||
|     return 7 | ||||
|   fi | ||||
| } | ||||
| 
 | ||||
| dap_dl() | ||||
| { | ||||
|   $http_get $http_opts $http_out "$2" "$1" | ||||
| } | ||||
| 
 | ||||
| dap_dl_bash() | ||||
| { | ||||
|   dap_url=$1 | ||||
|   #dap_args=$2 | ||||
|   rm -rf dap-tmp-runner.sh | ||||
|   $http_get $http_opts $http_out dap-tmp-runner.sh "$dap_url"; bash dap-tmp-runner.sh; rm dap-tmp-runner.sh | ||||
| } | ||||
| 
 | ||||
| detect_http_get | ||||
| 
 | ||||
| ############################### | ||||
| #                             # | ||||
| # actual script continues...  # | ||||
| #                             # | ||||
| ############################### | ||||
| 
 | ||||
| install_walnut() | ||||
| { | ||||
|   sudo mkdir -p /srv/walnut/{var,etc,packages,node_modules} | ||||
|   # www-data exists on linux, _www exists on mac OS | ||||
|   sudo chown -R $(whoami):www-data /srv/walnut || sudo chown -R $(whoami):_www /srv/walnut | ||||
|   if [ ! -d "/srv/walnut/core/" ]; then | ||||
|     git clone https://git.daplie.com/Daplie/walnut.js.git /srv/walnut/core | ||||
|   fi | ||||
|   pushd /srv/walnut/core | ||||
|     if [ ! -d "./.git/" ]; then | ||||
|       echo "'/srv/walnut/core' exists but is not a git repository... not sure what to do here..." | ||||
|     fi | ||||
|     git checkout v1 | ||||
|     git pull | ||||
|   popd | ||||
|   rm -rf /srv/walnut/core/node_modules | ||||
|   ln -sf ../node_modules /srv/walnut/core/node_modules | ||||
|   /srv/walnut/core/install-helper.sh /srv/walnut | ||||
|   # Now that the install is finished we need to set the owner to the user that will actually | ||||
|   # be running the walnut server. | ||||
|   sudo chown -R www-data:www-data /srv/walnut || sudo chown -R _www:_www /srv/walnut | ||||
| } | ||||
| 
 | ||||
| # Install node | ||||
| echo "v8.2.1" > /tmp/NODEJS_VER | ||||
| daplie-install-node-dev | ||||
| npm install -g npm@4 | ||||
| 
 | ||||
| # Install goldilocks | ||||
| daplie-install-goldilocks | ||||
| 
 | ||||
| # Install walnut | ||||
| install_walnut | ||||
| 
 | ||||
| echo "" | ||||
| echo "You must have some set of domain set up to properly use goldilocks+walnut:" | ||||
| echo "" | ||||
| echo "  example.com" | ||||
| echo "  www.example.com" | ||||
| echo "  api.example.com" | ||||
| echo "  assets.example.com" | ||||
| echo "  cloud.example.com" | ||||
| echo "  api.cloud.example.com" | ||||
| echo "" | ||||
| echo "Check the WALNUT README.md for more info and how to set up /etc/goldilocks/goldilocks.yml" | ||||
| echo "" | ||||
| @ -1,20 +0,0 @@ | ||||
| set -e | ||||
| set -u | ||||
| 
 | ||||
| my_name=walnut | ||||
| # TODO provide an option to supply my_ver and my_tmp | ||||
| my_ver=master | ||||
| my_tmp=$(mktemp -d) | ||||
| 
 | ||||
| mkdir -p $my_tmp/opt/$my_name/lib/node_modules/$my_name | ||||
| git clone https://git.daplie.com/Daplie/walnut.js.git $my_tmp/opt/$my_name/core | ||||
| 
 | ||||
| echo "Installing to $my_tmp (will be moved after install)" | ||||
| pushd $my_tmp/opt/$my_name/core | ||||
|   git checkout $my_ver | ||||
|   source ./installer/install.sh | ||||
| popd | ||||
| 
 | ||||
| echo "Installation successful, now cleaning up $my_tmp ..." | ||||
| rm -rf $my_tmp | ||||
| echo "Done" | ||||
| @ -1,48 +0,0 @@ | ||||
| ############################### | ||||
| #                             # | ||||
| #         http_get            # | ||||
| # boilerplate for curl / wget # | ||||
| #                             # | ||||
| ############################### | ||||
| 
 | ||||
| # See https://git.daplie.com/Daplie/daplie-snippets/blob/master/bash/http-get.sh | ||||
| 
 | ||||
| _h_http_get="" | ||||
| _h_http_opts="" | ||||
| _h_http_out="" | ||||
| 
 | ||||
| detect_http_get() | ||||
| { | ||||
|   set +e | ||||
|   if type -p curl >/dev/null 2>&1; then | ||||
|     _h_http_get="curl" | ||||
|     _h_http_opts="-fsSL" | ||||
|     _h_http_out="-o" | ||||
|   elif type -p wget >/dev/null 2>&1; then | ||||
|     _h_http_get="wget" | ||||
|     _h_http_opts="--quiet" | ||||
|     _h_http_out="-O" | ||||
|   else | ||||
|     echo "Aborted, could not find curl or wget" | ||||
|     return 7 | ||||
|   fi | ||||
|   set -e | ||||
| } | ||||
| 
 | ||||
| http_get() | ||||
| { | ||||
|   $_h_http_get $_h_http_opts $_h_http_out "$2" "$1" | ||||
|   touch "$2" | ||||
| } | ||||
| 
 | ||||
| http_bash() | ||||
| { | ||||
|   _http_url=$1 | ||||
|   #dap_args=$2 | ||||
|   rm -rf dap-tmp-runner.sh | ||||
|   $_h_http_get $_h_http_opts $_h_http_out dap-tmp-runner.sh "$_http_url"; bash dap-tmp-runner.sh; rm dap-tmp-runner.sh | ||||
| } | ||||
| 
 | ||||
| detect_http_get | ||||
| 
 | ||||
| ## END HTTP_GET ## | ||||
| @ -1,17 +0,0 @@ | ||||
| set -u | ||||
| 
 | ||||
| my_app_launchd_service="Library/LaunchDaemons/${my_app_pkg_name}.plist" | ||||
| 
 | ||||
| echo "" | ||||
| echo "Installing as launchd service" | ||||
| echo "" | ||||
| 
 | ||||
| # See http://www.launchd.info/ | ||||
| safe_copy_config "$my_app_dist/$my_app_launchd_service" "$my_root/$my_app_launchd_service" | ||||
| 
 | ||||
| $sudo_cmd chown root:wheel "$my_root/$my_app_launchd_service" | ||||
| 
 | ||||
| $sudo_cmd launchctl unload -w "$my_root/$my_app_launchd_service" >/dev/null 2>/dev/null | ||||
| $sudo_cmd launchctl load -w "$my_root/$my_app_launchd_service" | ||||
| 
 | ||||
| echo "$my_app_name started with launchd" | ||||
| @ -1,35 +0,0 @@ | ||||
| set -u | ||||
| 
 | ||||
| my_app_systemd_service="etc/systemd/system/${my_app_name}.service" | ||||
| my_app_systemd_tmpfiles="etc/tmpfiles.d/${my_app_name}.conf" | ||||
| 
 | ||||
| echo "" | ||||
| echo "Installing as systemd service" | ||||
| echo "" | ||||
| 
 | ||||
| sed "s/MY_USER/$my_user/g" "$my_app_dist/$my_app_systemd_service" > "$my_app_dist/$my_app_systemd_service.2" | ||||
| sed "s/MY_GROUP/$my_group/g" "$my_app_dist/$my_app_systemd_service.2" > "$my_app_dist/$my_app_systemd_service" | ||||
| rm "$my_app_dist/$my_app_systemd_service.2" | ||||
| safe_copy_config "$my_app_dist/$my_app_systemd_service" "$my_root/$my_app_systemd_service" | ||||
| 
 | ||||
| sed "s/MY_USER/$my_user/g" "$my_app_dist/$my_app_systemd_tmpfiles" > "$my_app_dist/$my_app_systemd_tmpfiles.2" | ||||
| sed "s/MY_GROUP/$my_group/g" "$my_app_dist/$my_app_systemd_tmpfiles.2" > "$my_app_dist/$my_app_systemd_tmpfiles" | ||||
| rm "$my_app_dist/$my_app_systemd_tmpfiles.2" | ||||
| safe_copy_config "$my_app_dist/$my_app_systemd_tmpfiles" "$my_root/$my_app_systemd_tmpfiles" | ||||
| 
 | ||||
| $sudo_cmd systemctl stop "${my_app_name}.service" >/dev/null 2>/dev/null || true | ||||
| $sudo_cmd systemctl daemon-reload | ||||
| $sudo_cmd systemctl start "${my_app_name}.service" | ||||
| $sudo_cmd systemctl enable "${my_app_name}.service" | ||||
| 
 | ||||
| echo "" | ||||
| echo "" | ||||
| echo "Fun systemd commands to remember:" | ||||
| echo "  $sudo_cmd systemctl daemon-reload" | ||||
| echo "  $sudo_cmd systemctl restart $my_app_name.service" | ||||
| echo "" | ||||
| echo "$my_app_name started with systemctl, check its status like so:" | ||||
| echo "  $sudo_cmd systemctl status $my_app_name" | ||||
| echo "  $sudo_cmd journalctl -xefu $my_app_name" | ||||
| echo "" | ||||
| echo "" | ||||
| @ -1,37 +0,0 @@ | ||||
| safe_copy_config() | ||||
| { | ||||
|   src=$1 | ||||
|   dst=$2 | ||||
|   $sudo_cmd mkdir -p $(dirname "$dst") | ||||
|   if [ -f "$dst" ]; then | ||||
|     $sudo_cmd rsync -a "$src" "$dst.latest" | ||||
|     # TODO edit config file with $my_user and $my_group | ||||
|     if [ "$(cat $dst)" == "$(cat $dst.latest)" ]; then | ||||
|       $sudo_cmd rm $dst.latest | ||||
|     else | ||||
|       echo "MANUAL INTERVENTION REQUIRED: check the systemd script update and manually decide what you want to do" | ||||
|       echo "diff $dst $dst.latest" | ||||
|       $sudo_cmd chown -R root:root "$dst.latest" | ||||
|     fi | ||||
|   else | ||||
|     $sudo_cmd rsync -a --ignore-existing "$src" "$dst" | ||||
|   fi | ||||
|   $sudo_cmd chown -R root:root "$dst" | ||||
|   $sudo_cmd chmod 644 "$dst" | ||||
| } | ||||
| 
 | ||||
| installable="" | ||||
| if [ -d "$my_root/etc/systemd/system" ]; then | ||||
|   source ./installer/install-for-systemd.sh | ||||
|   installable="true" | ||||
| fi | ||||
| if [ -d "/Library/LaunchDaemons" ]; then | ||||
|   source ./installer/install-for-launchd.sh | ||||
|   installable="true" | ||||
| fi | ||||
| if [ -z "$installable" ]; then | ||||
|   echo "" | ||||
|   echo "Unknown system service init type. You must install as a system service manually." | ||||
|   echo '(please file a bug with the output of "uname -a")' | ||||
|   echo "" | ||||
| fi | ||||
| @ -1,194 +0,0 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| set -e | ||||
| set -u | ||||
| 
 | ||||
| ### IMPORTANT ### | ||||
| ###  VERSION  ### | ||||
| my_name=walnut | ||||
| my_app_pkg_name=com.daplie.walnut.web | ||||
| my_app_ver="v1.2" | ||||
| my_azp_oauth3_ver="v1.2" | ||||
| export NODE_VERSION="v8.9.0" | ||||
| 
 | ||||
| if [ -z "${my_tmp-}" ]; then | ||||
|   my_tmp="$(mktemp -d)" | ||||
|   mkdir -p $my_tmp/opt/$my_name/core | ||||
|   echo "Installing to $my_tmp (will be moved after install)" | ||||
|   git clone ./ $my_tmp/opt/$my_name/core | ||||
|   pushd $my_tmp/opt/$my_name/core | ||||
| fi | ||||
| 
 | ||||
| ################# | ||||
| 
 | ||||
| ### IMPORTANT ### | ||||
| ###  VERSION  ### | ||||
| #my_app_ver="v1.1" | ||||
| my_app_ver="installer-v2" | ||||
| my_launchpad_ver="v1.2" | ||||
| my_azp_oauth3_ver="v1.1.3" | ||||
| my_iss_oauth3_rest_ver="v1.2.0" | ||||
| my_iss_oauth3_pages_ver="v1.2.1" | ||||
| my_www_daplie_ver=v1.0.15 | ||||
| export NODE_VERSION="v8.9.0" | ||||
| ################# | ||||
| export NODE_PATH=$my_tmp/opt/$my_name/lib/node_modules | ||||
| export PATH=$my_tmp/opt/$my_name/bin/:$PATH | ||||
| export NPM_CONFIG_PREFIX=$my_tmp/opt/$my_name | ||||
| my_npm="$NPM_CONFIG_PREFIX/bin/npm" | ||||
| ################# | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # TODO un-hardcode core at al | ||||
| #my_app_dist=$my_tmp/opt/$my_name/lib/node_modules/$my_name/dist | ||||
| my_app_dist=$my_tmp/opt/$my_name/core/dist | ||||
| installer_base="https://git.daplie.com/Daplie/goldilocks.js/raw/$my_app_ver" | ||||
| 
 | ||||
| # Backwards compat | ||||
| # some scripts still use the old names | ||||
| my_app_dir=$my_tmp | ||||
| my_app_name=$my_name | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| git checkout $my_app_ver | ||||
| 
 | ||||
| mkdir -p $my_tmp/{etc,opt,srv,var}/$my_name | ||||
| mkdir -p "$my_tmp/var/log/$my_name" | ||||
| mkdir -p "$my_tmp/opt/$my_name"/{bin,config,core,etc,lib,node_modules,var} | ||||
| ln -s ../core/bin/$my_name.js $my_tmp/opt/$my_name/bin/$my_name | ||||
| ln -s ../core/bin/$my_name.js $my_tmp/opt/$my_name/bin/$my_name.js | ||||
| #ln -s ../lib/node_modules/$my_name/bin/$my_name.js $my_tmp/opt/$my_name/bin/$my_name | ||||
| #ln -s ../lib/node_modules/$my_name/bin/$my_name.js $my_tmp/opt/$my_name/bin/$my_name.js | ||||
| mkdir -p "$my_tmp/opt/$my_name"/packages/{api,pages,rest,services} | ||||
| mkdir -p "$my_tmp/opt/$my_name"/etc/client-api-grants | ||||
| # TODO move packages and sites to /srv, grants to /etc | ||||
| ln -s ../etc/client-api-grants "$my_tmp/opt/$my_name"/packages/client-api-grants | ||||
| mkdir -p "$my_tmp/opt/$my_name"/var/sites | ||||
| ln -s ../var/sites "$my_tmp/opt/$my_name"/packages/sites | ||||
| mkdir -p "$my_tmp/etc/$my_name" | ||||
| chmod 775 "$my_tmp/etc/$my_name" | ||||
| cat "$my_app_dist/etc/$my_name/$my_name.example.yml" > "$my_tmp/etc/$my_name/$my_name.example.yml" | ||||
| chmod 664 "$my_tmp/etc/$my_name/$my_name.example.yml" | ||||
| mkdir -p $my_tmp/var/log/$my_name | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # Helpers | ||||
| # | ||||
| source ./installer/sudo-cmd.sh | ||||
| source ./installer/http-get.sh | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # Dependencies | ||||
| # | ||||
| echo $NODE_VERSION > /tmp/NODEJS_VER | ||||
| # This will read the NODE_* and PATH variables set previously, as well as /tmp/NODEJS_VER | ||||
| http_bash "https://git.coolaj86.com/coolaj86/node-installer.sh/raw/v1.1/install.sh" | ||||
| $my_npm install -g npm@4 | ||||
| $my_npm install -g bower | ||||
| touch $my_tmp/opt/$my_name/.bowerrc | ||||
| echo '{ "allow_root": true }' > $my_tmp/opt/$my_name/.bowerrc | ||||
| 
 | ||||
| #pushd $my_tmp/opt/$my_name/lib/node_modules/$my_name | ||||
| pushd $my_tmp/opt/$my_name/core | ||||
|   mkdir -p ../node_modules | ||||
|   ln -s ../node_modules node_modules | ||||
|   $my_npm install | ||||
| popd | ||||
| 
 | ||||
| git clone https://git.daplie.com/Daplie/walnut_launchpad.git $my_tmp/opt/$my_name/core/lib/walnut@daplie.com/setup | ||||
| pushd $my_tmp/opt/$my_name/core/lib/walnut@daplie.com/setup | ||||
|   git pull | ||||
|   git checkout $my_launchpad_ver | ||||
| 
 | ||||
|   git clone https://git.daplie.com/OAuth3/oauth3.js.git ./assets/oauth3.org | ||||
|   pushd assets/oauth3.org | ||||
|     git checkout $my_azp_oauth3_ver | ||||
|   popd | ||||
| popd | ||||
| 
 | ||||
| pushd $my_tmp/opt/$my_name/packages | ||||
|   git clone https://git.daplie.com/OAuth3/issuer_oauth3.org.git rest/issuer@oauth3.org | ||||
|   pushd rest/issuer@oauth3.org/ | ||||
|       git checkout $my_iss_oauth3_rest_ver | ||||
|       $my_npm install | ||||
|   popd | ||||
| 
 | ||||
|   git clone https://git.daplie.com/OAuth3/org.oauth3.git pages/issuer@oauth3.org | ||||
|   pushd pages/issuer@oauth3.org | ||||
|     git checkout $my_iss_oauth3_pages_ver | ||||
|     bash ./install.sh | ||||
| 
 | ||||
|     pushd ./assets/oauth3.org | ||||
|       git checkout $my_azp_oauth3_ver | ||||
|     popd | ||||
|   popd | ||||
| 
 | ||||
|   git clone https://git.daplie.com/Daplie/walnut_rest_www_daplie.com.git rest/www@daplie.com | ||||
|   pushd rest/www@daplie.com | ||||
|     git checkout $my_www_daplie_ver | ||||
|     $my_npm install | ||||
|   popd | ||||
| popd | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # System Service | ||||
| # | ||||
| source ./installer/my-root.sh | ||||
| echo "Pre-installation to $my_tmp complete, now installing to $my_root/ ..." | ||||
| set +e | ||||
| if type -p tree >/dev/null 2>/dev/null; then | ||||
|   #tree -I "node_modules|include|share" $my_tmp | ||||
|   tree -L 6 -I "include|share|npm" $my_tmp | ||||
| else | ||||
|   ls $my_tmp | ||||
| fi | ||||
| set -e | ||||
| 
 | ||||
| source ./installer/my-user-my-group.sh | ||||
| echo "User $my_user Group $my_group" | ||||
| 
 | ||||
| $sudo_cmd chown -R $my_user:$my_group $my_tmp | ||||
| $sudo_cmd chown root:root $my_tmp/* | ||||
| $sudo_cmd chown root:root $my_tmp | ||||
| $sudo_cmd chmod 0755 $my_tmp | ||||
| $sudo_cmd rsync -a --ignore-existing $my_tmp/ $my_root/ | ||||
| $sudo_cmd rsync -a --ignore-existing $my_app_dist/etc/$my_name/$my_name.yml $my_root/etc/$my_name/$my_name.yml | ||||
| source ./installer/install-system-service.sh | ||||
| 
 | ||||
| # Change to admin perms | ||||
| $sudo_cmd chown -R $my_user:$my_group $my_root/opt/$my_name | ||||
| $sudo_cmd chown -R $my_user:$my_group $my_root/var/www $my_root/srv/www | ||||
| 
 | ||||
| # make sure the files are all read/write for the owner and group, and then set | ||||
| # the setuid and setgid bits so that any files/directories created inside these | ||||
| # directories have the same owner and group. | ||||
| $sudo_cmd chmod -R ug+rwX $my_root/opt/$my_name | ||||
| find $my_root/opt/$my_name -type d -exec $sudo_cmd chmod ug+s {} \; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| echo "" | ||||
| echo "You must have some set of domain set up to properly use goldilocks+walnut:" | ||||
| echo "" | ||||
| echo "  example.com" | ||||
| echo "  www.example.com" | ||||
| echo "  api.example.com" | ||||
| echo "  assets.example.com" | ||||
| echo "  cloud.example.com" | ||||
| echo "  api.cloud.example.com" | ||||
| echo "" | ||||
| echo "Check the WALNUT README.md for more info and how to set up /etc/goldilocks/goldilocks.yml" | ||||
| echo "" | ||||
| echo "Unistall: rm -rf /srv/walnut/ /var/walnut/ /etc/walnut/ /opt/walnut/ /var/log/walnut/ /etc/systemd/system/walnut.service /etc/tmpfiles.d/walnut.conf" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| rm -rf $my_tmp | ||||
| @ -1,8 +0,0 @@ | ||||
| # something or other about android and tmux using PREFIX | ||||
| #: "${PREFIX:=''}" | ||||
| my_root="" | ||||
| if [ -z "${PREFIX-}" ]; then | ||||
|   my_root="" | ||||
| else | ||||
|   my_root="$PREFIX" | ||||
| fi | ||||
| @ -1,19 +0,0 @@ | ||||
| if type -p adduser >/dev/null 2>/dev/null; then | ||||
|   if [ -z "$(cat $my_root/etc/passwd | grep $my_app_name)" ]; then | ||||
|     $sudo_cmd adduser --home $my_root/opt/$my_app_name --gecos '' --disabled-password $my_app_name | ||||
|   fi | ||||
|   my_user=$my_app_name | ||||
|   my_group=$my_app_name | ||||
| elif [ -n "$(cat /etc/passwd | grep www-data:)" ]; then | ||||
|   # Linux (Ubuntu) | ||||
|   my_user=www-data | ||||
|   my_group=www-data | ||||
| elif [ -n "$(cat /etc/passwd | grep _www:)" ]; then | ||||
|   # Mac | ||||
|   my_user=_www | ||||
|   my_group=_www | ||||
| else | ||||
|   # Unsure | ||||
|   my_user=$(whoami) | ||||
|   my_group=$(id -g -n) | ||||
| fi | ||||
| @ -1,7 +0,0 @@ | ||||
| # Not every platform has or needs sudo, gotta save them O(1)s... | ||||
| sudo_cmd="" | ||||
| set +e | ||||
| if type -p sudo >/dev/null 2>/dev/null; then | ||||
|   ((EUID)) && [[ -z "${ANDROID_ROOT-}" ]] && sudo_cmd="sudo" | ||||
| fi | ||||
| set -e | ||||
							
								
								
									
										1391
									
								
								lib/apis.js
									
									
									
									
									
								
							
							
						
						
									
										1391
									
								
								lib/apis.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,21 +1,20 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| function rejectableRequest(req, res, promise, msg) { | ||||
| module.exports.rejectableRequest = function rejectableRequest(req, res, promise, msg) { | ||||
|   return promise.error(function (err) { | ||||
|     res.error(err); | ||||
|   }).catch(function (err) { | ||||
|     console.error('[ERROR] \'' + msg + '\''); | ||||
|     // The stack contains the message as well, so no need to log the message when we log the stack
 | ||||
|     console.error(err.stack || err.message || JSON.stringify(err)); | ||||
|     console.error(err.message); | ||||
|     console.error(err.stack); | ||||
| 
 | ||||
|     res.error(err); | ||||
|   }); | ||||
| } | ||||
| module.exports.rejectableRequest = rejectableRequest; | ||||
| }; | ||||
| 
 | ||||
| module.exports.promisableRequest = | ||||
| module.exports.promiseRequest = function promiseRequest(req, res, promise, msg) { | ||||
|   promise = promise.then(function (result) { | ||||
|   return promise.then(function (result) { | ||||
|     if (result._cache) { | ||||
|       res.setHeader('Cache-Control', 'public, max-age=' + (result._cache / 1000)); | ||||
|       res.setHeader('Expires', new Date(Date.now() + result._cache).toUTCString()); | ||||
| @ -27,7 +26,13 @@ module.exports.promiseRequest = function promiseRequest(req, res, promise, msg) | ||||
|       result = result._value; | ||||
|     } | ||||
|     res.send(result); | ||||
|   }); | ||||
|   }).error(function (err) { | ||||
|     res.error(err); | ||||
|   }).catch(function (err) { | ||||
|     console.error('[ERROR] \'' + msg + '\''); | ||||
|     console.error(err.message); | ||||
|     console.error(err.stack); | ||||
| 
 | ||||
|   return rejectableRequest(req, res, promise, msg); | ||||
|     res.error(err); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
							
								
								
									
										26
									
								
								lib/main.js
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								lib/main.js
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi, errorIfAssets) { | ||||
| module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi) { | ||||
|   var PromiseA = require('bluebird'); | ||||
|   var path = require('path'); | ||||
|   var fs = PromiseA.promisifyAll(require('fs')); | ||||
| @ -293,27 +293,10 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi | ||||
|   // TODO handle assets.example.com/sub/assets/com.example.xyz/
 | ||||
| 
 | ||||
|   app.use('/api', require('connect-send-error').error()); | ||||
|   app.use('/assets', require('connect-send-error').error()); | ||||
|   app.use('/', function (req, res, next) { | ||||
|     // If this doesn't look like an API or assets we can move along
 | ||||
| 
 | ||||
|     /* | ||||
|     console.log('.'); | ||||
|     console.log('[main.js] req.url, req.hostname'); | ||||
|     console.log(req.url); | ||||
|     console.log(req.hostname); | ||||
|     console.log('.'); | ||||
|     */ | ||||
| 
 | ||||
|     if (!/\/(api|assets)(\/|$)/.test(req.url)) { | ||||
|       //console.log('[main.js] api|assets');
 | ||||
|       next(); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // keep https://assets.example.com/assets but skip https://example.com/assets
 | ||||
|     if (/\/assets(\/|$)/.test(req.url) && !/(^|\.)(api|assets)(\.)/.test(req.hostname) && !/^[0-9\.]+$/.test(req.hostname)) { | ||||
|       //console.log('[main.js] skip');
 | ||||
|     // If this doesn't look like an API we can move along
 | ||||
|     if (!/\/api(\/|$)/.test(req.url)) { | ||||
|       // /^api\./.test(req.hostname) &&
 | ||||
|       next(); | ||||
|       return; | ||||
|     } | ||||
| @ -342,7 +325,6 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi | ||||
|     return; | ||||
|   }); | ||||
|   app.use('/', errorIfApi); | ||||
|   app.use('/', errorIfAssets); | ||||
|   app.use('/', serveStatic); | ||||
|   app.use('/', serveApps); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										306
									
								
								lib/oauth3.js
									
									
									
									
									
								
							
							
						
						
									
										306
									
								
								lib/oauth3.js
									
									
									
									
									
								
							| @ -1,306 +0,0 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var PromiseA = require('bluebird'); | ||||
| 
 | ||||
| function generateRescope(req, Models, decoded, fullPpid, ppid) { | ||||
|   return function (/*sub*/) { | ||||
|     // TODO: this function is supposed to convert PPIDs of different parties to some account
 | ||||
|     // ID that allows application to keep track of permisions and what-not.
 | ||||
|     console.log('[rescope] Attempting ', fullPpid); | ||||
|     return Models.IssuerOauth3OrgGrants.find({ azpSub: fullPpid }).then(function (results) { | ||||
|       if (results[0]) { | ||||
|         console.log('[rescope] lukcy duck: got it on the 1st try'); | ||||
|         return PromiseA.resolve(results); | ||||
|       } | ||||
| 
 | ||||
|       // XXX BUG XXX
 | ||||
|       // should be able to distinguish between own ids and 3rd party via @whatever.com
 | ||||
|       return Models.IssuerOauth3OrgGrants.find({ azpSub: ppid }); | ||||
|     }).then(function (results) { | ||||
|       var result = results[0]; | ||||
| 
 | ||||
|       if (!result || !result.sub || !decoded.iss) { | ||||
|         // XXX BUG XXX TODO swap this external ppid for an internal (and ask user to link with existing profile)
 | ||||
|         //req.oauth3.accountIdx = fullPpid;
 | ||||
|         throw new Error("internal / external ID swapping not yet implemented. TODO: " | ||||
|           + "No profile found with that credential. Would you like to create a new profile or link to an existing profile?"); | ||||
|       } | ||||
| 
 | ||||
|       // XXX BUG XXX need to pass own url in to use as issuer for own tokens
 | ||||
|       req.oauth3.accountIdx = result.sub + '@' + decoded.iss; | ||||
| 
 | ||||
|       console.log('[rescope] result:'); | ||||
|       console.log(results); | ||||
|       console.log(req.oauth3.accountIdx); | ||||
| 
 | ||||
|       return PromiseA.resolve(req.oauth3.accountIdx); | ||||
|     }); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function extractAccessToken(req) { | ||||
|   var token = null; | ||||
|   var parts; | ||||
|   var scheme; | ||||
|   var credentials; | ||||
| 
 | ||||
|   if (req.headers && req.headers.authorization) { | ||||
|     // Works for all of Authorization: Bearer {{ token }}, Token {{ token }}, JWT {{ token }}
 | ||||
|     parts = req.headers.authorization.split(' '); | ||||
| 
 | ||||
|     if (parts.length !== 2) { | ||||
|       return PromiseA.reject(new Error("malformed Authorization header")); | ||||
|     } | ||||
| 
 | ||||
|     scheme = parts[0]; | ||||
|     credentials = parts[1]; | ||||
| 
 | ||||
|     if (-1 !== ['token', 'bearer'].indexOf(scheme.toLowerCase())) { | ||||
|       token = credentials; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (req.body && req.body.access_token) { | ||||
|     if (token) { PromiseA.reject(new Error("token exists in header and body")); } | ||||
|     token = req.body.access_token; | ||||
|   } | ||||
| 
 | ||||
|   // TODO disallow query with req.method === 'GET'
 | ||||
|   // NOTE: the case of DDNS on routers requires a GET and access_token
 | ||||
|   // (cookies should be used for protected static assets)
 | ||||
|   if (req.query && req.query.access_token) { | ||||
|     if (token) { PromiseA.reject(new Error("token already exists in either header or body and also in query")); } | ||||
|     token = req.query.access_token; | ||||
|   } | ||||
| 
 | ||||
|   /* | ||||
|   err = new Error(challenge()); | ||||
|   err.code = 'E_BEARER_REALM'; | ||||
| 
 | ||||
|   if (!token) { return PromiseA.reject(err); } | ||||
|   */ | ||||
| 
 | ||||
|   return PromiseA.resolve(token); | ||||
| } | ||||
| 
 | ||||
| function verifyToken(token) { | ||||
|   var jwt = require('jsonwebtoken'); | ||||
|   var decoded; | ||||
| 
 | ||||
|   if (!token) { | ||||
|     return PromiseA.reject({ | ||||
|       message: 'no token provided' | ||||
|     , code: 'E_NO_TOKEN' | ||||
|     , url: 'https://oauth3.org/docs/errors#E_NO_TOKEN' | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   try { | ||||
|     decoded = jwt.decode(token, {complete: true}); | ||||
|   } catch (e) {} | ||||
|   if (!decoded) { | ||||
|     return PromiseA.reject({ | ||||
|       message: 'provided token not a JSON Web Token' | ||||
|     , code: 'E_NOT_JWT' | ||||
|     , url: 'https://oauth3.org/docs/errors#E_NOT_JWT' | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   var sub = decoded.payload.sub || decoded.payload.ppid || decoded.payload.appScopedId; | ||||
|   if (!sub) { | ||||
|     return PromiseA.reject({ | ||||
|       message: 'token missing sub' | ||||
|     , code: 'E_MISSING_SUB' | ||||
|     , url: 'https://oauth3.org/docs/errors#E_MISSING_SUB' | ||||
|     }); | ||||
|   } | ||||
|   var kid = decoded.header.kid || decoded.payload.kid; | ||||
|   if (!kid) { | ||||
|     return PromiseA.reject({ | ||||
|       message: 'token missing kid' | ||||
|     , code: 'E_MISSING_KID' | ||||
|     , url: 'https://oauth3.org/docs/errors#E_MISSING_KID' | ||||
|     }); | ||||
|   } | ||||
|   if (!decoded.payload.iss) { | ||||
|     return PromiseA.reject({ | ||||
|       message: 'token missing iss' | ||||
|     , code: 'E_MISSING_ISS' | ||||
|     , url: 'https://oauth3.org/docs/errors#E_MISSING_ISS' | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   var OAUTH3 = require('oauth3.js'); | ||||
|   OAUTH3._hooks = require('oauth3.js/oauth3.node.storage.js'); | ||||
|   return OAUTH3.discover(decoded.payload.iss).then(function (directives) { | ||||
|     var args = (directives || {}).retrieve_jwk; | ||||
|     if (typeof args === 'string') { | ||||
|       args = { url: args, method: 'GET' }; | ||||
|     } | ||||
|     if (typeof (args || {}).url !== 'string') { | ||||
|       return PromiseA.reject({ | ||||
|         message: 'token issuer does not support retrieving JWKs' | ||||
|       , code: 'E_INVALID_ISS' | ||||
|       , url: 'https://oauth3.org/docs/errors#E_INVALID_ISS' | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     var params = { | ||||
|       sub: sub | ||||
|     , kid: kid | ||||
|     }; | ||||
|     var url = args.url; | ||||
|     var body; | ||||
|     Object.keys(params).forEach(function (key) { | ||||
|       if (url.indexOf(':'+key) !== -1) { | ||||
|         url = url.replace(':'+key, params[key]); | ||||
|         delete params[key]; | ||||
|       } | ||||
|     }); | ||||
|     if (Object.keys(params).length > 0) { | ||||
|       if ('GET' === (args.method || 'GET').toUpperCase()) { | ||||
|         url += '?' + OAUTH3.query.stringify(params); | ||||
|       } else { | ||||
|         body = params; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return OAUTH3.request({ | ||||
|       url: OAUTH3.url.resolve(directives.api, url) | ||||
|     , method: args.method | ||||
|     , data: body | ||||
|     }).catch(function (err) { | ||||
|       return PromiseA.reject({ | ||||
|         message: 'failed to retrieve public key from token issuer' | ||||
|       , code: 'E_NO_PUB_KEY' | ||||
|       , url: 'https://oauth3.org/docs/errors#E_NO_PUB_KEY' | ||||
|       , subErr: err.toString() | ||||
|       }); | ||||
|     }); | ||||
|   }, function (err) { | ||||
|     return PromiseA.reject({ | ||||
|       message: 'token issuer is not a valid OAuth3 provider' | ||||
|     , code: 'E_INVALID_ISS' | ||||
|     , url: 'https://oauth3.org/docs/errors#E_INVALID_ISS' | ||||
|     , subErr: err.toString() | ||||
|     }); | ||||
|   }).then(function (res) { | ||||
|     if (res.data.error) { | ||||
|       return PromiseA.reject(res.data.error); | ||||
|     } | ||||
|     var opts = {}; | ||||
|     if (Array.isArray(res.data.alg)) { | ||||
|       opts.algorithms = res.data.alg; | ||||
|     } else if (typeof res.data.alg === 'string') { | ||||
|       opts.algorithms = [res.data.alg]; | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       return jwt.verify(token, require('jwk-to-pem')(res.data), opts); | ||||
|     } catch (err) { | ||||
|       return PromiseA.reject({ | ||||
|         message: 'token verification failed' | ||||
|       , code: 'E_INVALID_TOKEN' | ||||
|       , url: 'https://oauth3.org/docs/errors#E_INVALID_TOKEN' | ||||
|       , subErr: err.toString() | ||||
|       }); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function deepFreeze(obj) { | ||||
|   Object.keys(obj).forEach(function (key) { | ||||
|     if (obj[key] && typeof obj[key] === 'object') { | ||||
|       deepFreeze(obj[key]); | ||||
|     } | ||||
|   }); | ||||
|   Object.freeze(obj); | ||||
| } | ||||
| 
 | ||||
| function cookieOauth3(Models, req, res, next) { | ||||
|   req.oauth3 = {}; | ||||
| 
 | ||||
|   var token = req.cookies.jwt; | ||||
| 
 | ||||
|   req.oauth3.encodedToken = token; | ||||
|   req.oauth3.verifyAsync = function (jwt) { | ||||
|     return verifyToken(jwt || token); | ||||
|   }; | ||||
| 
 | ||||
|   return verifyToken(token).then(function  (decoded) { | ||||
|     req.oauth3.token = decoded; | ||||
|     if (!decoded) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     var ppid = decoded.sub || decoded.ppid || decoded.appScopedId; | ||||
|     req.oauth3.ppid = ppid; | ||||
|     req.oauth3.accountIdx = ppid+'@'+decoded.iss; | ||||
| 
 | ||||
|     var hash = require('crypto').createHash('sha256').update(req.oauth3.accountIdx).digest('base64'); | ||||
|     hash = hash.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+/g, ''); | ||||
|     req.oauth3.accountHash = hash; | ||||
| 
 | ||||
|     req.oauth3.rescope = generateRescope(req, Models, decoded, fullPpid, ppid); | ||||
|   }).then(function () { | ||||
|     deepFreeze(req.oauth3); | ||||
|     //Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
 | ||||
|     next(); | ||||
|   }, function (err) { | ||||
|     if ('E_NO_TOKEN' === err.code) { | ||||
|       next(); | ||||
|       return; | ||||
|     } | ||||
|     console.error('[walnut] cookie lib/oauth3 error:'); | ||||
|     console.error(err); | ||||
|     res.send(err); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| function attachOauth3(Models, req, res, next) { | ||||
|   req.oauth3 = {}; | ||||
| 
 | ||||
|   extractAccessToken(req).then(function (token) { | ||||
|     req.oauth3.encodedToken = token; | ||||
|     req.oauth3.verifyAsync = function (jwt) { | ||||
|       return verifyToken(jwt || token); | ||||
|     }; | ||||
| 
 | ||||
|     if (!token) { | ||||
|       return null; | ||||
|     } | ||||
|     return verifyToken(token); | ||||
|   }).then(function  (decoded) { | ||||
|     req.oauth3.token = decoded; | ||||
|     if (!decoded) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     var ppid = decoded.sub || decoded.ppid || decoded.appScopedId; | ||||
|     var fullPpid = ppid+'@'+decoded.iss; | ||||
|     req.oauth3.ppid = ppid; | ||||
| 
 | ||||
|     // TODO we can anonymize the relationship between our user as the other service's user
 | ||||
|     // in our own database by hashing the remote service's ppid and using that as the lookup
 | ||||
|     var hash = require('crypto').createHash('sha256').update(fullPpid).digest('base64'); | ||||
|     hash = hash.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+/g, ''); | ||||
|     req.oauth3.accountHash = hash; | ||||
| 
 | ||||
|     req.oauth3.rescope = generateRescope(req, Models, decoded, fullPpid, ppid); | ||||
| 
 | ||||
|     console.log('############### assigned req.oauth3:'); | ||||
|     console.log(req.oauth3); | ||||
|   }).then(function () { | ||||
|     //deepFreeze(req.oauth3);
 | ||||
|     //Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
 | ||||
|     next(); | ||||
|   }, function (err) { | ||||
|     console.error('[walnut] JWT lib/oauth3 error:'); | ||||
|     console.error(err); | ||||
|     res.send(err); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| module.exports.attachOauth3 = attachOauth3; | ||||
| module.exports.cookieOauth3 = cookieOauth3; | ||||
| module.exports.verifyToken = verifyToken; | ||||
| @ -55,7 +55,19 @@ function getApi(conf, pkgConf, pkgDeps, packagedApi) { | ||||
|       packagedApi._api = require('express-lazy')(); | ||||
|       packagedApi._api_app = myApp; | ||||
| 
 | ||||
|       packagedApi._api.use('/', require('./oauth3').attachOauth3); | ||||
|       //require('./oauth3-auth').inject(conf, packagedApi._api, pkgConf, pkgDeps);
 | ||||
|       pkgDeps.getOauth3Controllers = | ||||
|       packagedApi._getOauth3Controllers = require('oauthcommon/example-oauthmodels').create(conf).getControllers; | ||||
|       require('oauthcommon').inject(packagedApi._getOauth3Controllers, packagedApi._api, pkgConf, pkgDeps); | ||||
| 
 | ||||
|       // DEBUG
 | ||||
|       //
 | ||||
|       /* | ||||
|       packagedApi._api.use('/', function (req, res, next) { | ||||
|         console.log('[DEBUG pkgApiApp]', req.method, req.hostname, req.url); | ||||
|         next(); | ||||
|       }); | ||||
|       //*/
 | ||||
| 
 | ||||
|       // TODO fix backwards compat
 | ||||
| 
 | ||||
|  | ||||
| @ -150,21 +150,6 @@ module.exports.create = function (webserver, xconfx, state) { | ||||
|             models: models | ||||
|             // TODO don't let packages use this directly
 | ||||
|           , Promise: PromiseA | ||||
|           , dns: PromiseA.promisifyAll(require('dns')) | ||||
|           , crypto: PromiseA.promisifyAll(require('crypto')) | ||||
|           , fs: PromiseA.promisifyAll(require('fs')) | ||||
|           , path: require('path') | ||||
|           , validate: { | ||||
|               isEmail: function (email) { | ||||
|                 return /@/.test(email) && !/\s+/.test(email); | ||||
|               } | ||||
|             , email: function (email) { | ||||
|                 if (apiDeps.validate.isEmail(email)) { | ||||
|                   return null; | ||||
|                 } | ||||
|                 return new Error('invalid email address'); | ||||
|               } | ||||
|             } | ||||
|           }; | ||||
|           var apiFactories = { | ||||
|             memstoreFactory: { create: scopeMemstore } | ||||
| @ -195,7 +180,7 @@ module.exports.create = function (webserver, xconfx, state) { | ||||
|           function setupMain() { | ||||
|             if (xconfx.debug) { console.log('[main] setup'); } | ||||
|             mainApp = express(); | ||||
|             require('./main').create(mainApp, xconfx, apiFactories, apiDeps, errorIfApi, errorIfAssets).then(function () { | ||||
|             require('./main').create(mainApp, xconfx, apiFactories, apiDeps, errorIfApi).then(function () { | ||||
|               if (xconfx.debug) { console.log('[main] ready'); } | ||||
|               // TODO process.send({});
 | ||||
|             }); | ||||
| @ -240,24 +225,6 @@ module.exports.create = function (webserver, xconfx, state) { | ||||
|             next(); | ||||
|           } | ||||
| 
 | ||||
|           function errorIfNotAssets(req, res, next) { | ||||
|             var hostname = req.hostname || req.headers.host; | ||||
| 
 | ||||
|             if (!/^assets\.[a-z0-9\-]+/.test(hostname)) { | ||||
|               res.send({ error: | ||||
|                { message: "['" + hostname + req.url + "'] protected asset access is restricted to proper 'asset'-prefixed lowercase subdomains." | ||||
|                    + " The HTTP 'Host' header must exist and must begin with 'assets.' as in 'assets.example.com'." | ||||
|                    + " For development you may test with assets.localhost.daplie.me (or any domain by modifying your /etc/hosts)" | ||||
|                , code: 'E_NOT_API' | ||||
|                , _hostname: hostname | ||||
|                } | ||||
|               }); | ||||
|               return; | ||||
|             } | ||||
| 
 | ||||
|             next(); | ||||
|           } | ||||
| 
 | ||||
|           function errorIfApi(req, res, next) { | ||||
|             if (!/^api\./.test(req.headers.host)) { | ||||
|               next(); | ||||
| @ -273,25 +240,7 @@ module.exports.create = function (webserver, xconfx, state) { | ||||
|               return; | ||||
|             } | ||||
| 
 | ||||
|             res.send({ error: { code: 'E_NO_IMPL', message: "API not implemented" } }); | ||||
|           } | ||||
| 
 | ||||
|           function errorIfAssets(req, res, next) { | ||||
|             if (!/^assets\./.test(req.headers.host)) { | ||||
|               next(); | ||||
|               return; | ||||
|             } | ||||
| 
 | ||||
|             // has api. hostname prefix
 | ||||
| 
 | ||||
|             // doesn't have /api url prefix
 | ||||
|             if (!/^\/assets\//.test(req.url)) { | ||||
|               console.log('[walnut/worker assets] req.url', req.url); | ||||
|               res.send({ error: { message: "missing /assets/ url prefix" } }); | ||||
|               return; | ||||
|             } | ||||
| 
 | ||||
|             res.send({ error: { code: 'E_NO_IMPL', message: "assets handler not implemented" } }); | ||||
|             res.send({ error: { code: 'E_NO_IMPL', message: "not implemented" } }); | ||||
|           } | ||||
| 
 | ||||
|           app.disable('x-powered-by'); | ||||
| @ -309,11 +258,8 @@ module.exports.create = function (webserver, xconfx, state) { | ||||
|           })); | ||||
|           app.use('/api', recase); | ||||
| 
 | ||||
|           var cookieParser = require('cookie-parser'); // signing is done in JWT
 | ||||
| 
 | ||||
|           app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']); | ||||
|           app.use('/api', errorIfNotApi); | ||||
|           app.use('/assets', /*errorIfNotAssets,*/ cookieParser()); // serializer { path: '/assets', httpOnly: true, sameSite: true/*, domain: assets.example.com*/ }
 | ||||
|           app.use('/', function (req, res) { | ||||
|             if (!(req.encrypted || req.secure)) { | ||||
|               // did not come from https
 | ||||
|  | ||||
							
								
								
									
										20
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								package.json
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "walnut", | ||||
|   "version": "1.2.5", | ||||
|   "version": "0.1.0", | ||||
|   "description": "zero-config home cloud server", | ||||
|   "main": "walnut.js", | ||||
|   "scripts": { | ||||
| @ -34,7 +34,7 @@ | ||||
|     "public" | ||||
|   ], | ||||
|   "author": "AJ ONeal <aj@daplie.com> (https://daplie.com)", | ||||
|   "license": "(MIT or Apache2)", | ||||
|   "license": "Apache2", | ||||
|   "bugs": { | ||||
|     "url": "https://github.com/Daplie/walnut/issues" | ||||
|   }, | ||||
| @ -42,33 +42,29 @@ | ||||
|   "dependencies": { | ||||
|     "bluebird": "3.x", | ||||
|     "body-parser": "1.x", | ||||
|     "cluster-store": "2.0.x", | ||||
|     "cluster-store": "git+https://git.daplie.com/Daplie/cluster-store.git#v2", | ||||
|     "connect": "3.x", | ||||
|     "connect-cors": "0.5.x", | ||||
|     "connect-recase": "^1.0.2", | ||||
|     "connect-send-error": "1.x", | ||||
|     "cookie-parser": "^1.4.3", | ||||
|     "escape-html": "^1.0.2", | ||||
|     "escape-string-regexp": "1.x", | ||||
|     "express": "4.x", | ||||
|     "express-lazy": "^1.1.1", | ||||
|     "express-session": "^1.11.3", | ||||
|     "jsonwebtoken": "^7.4.1", | ||||
|     "jwk-to-pem": "^1.2.6", | ||||
|     "mailchimp-api-v3": "^1.7.0", | ||||
|     "mandrill-api": "^1.0.45", | ||||
|     "masterquest-sqlite3": "1.0.x", | ||||
|     "masterquest-sqlite3": "git+https://git.daplie.com/node/masterquest-sqlite3.git", | ||||
|     "mkdirp": "^0.5.1", | ||||
|     "multiparty": "^4.1.3", | ||||
|     "nodemailer": "^1.4.0", | ||||
|     "nodemailer-mailgun-transport": "1.x", | ||||
|     "oauth3.js": "git+https://git.oauth3.org/OAuth3/oauth3.js.git", | ||||
|     "recase": "^1.0.4", | ||||
|     "oauthcommon": "git+https://git.daplie.com/node/oauthcommon.git", | ||||
|     "request": "^2.81.0", | ||||
|     "scmp": "^2.0.0", | ||||
|     "serve-static": "1.x", | ||||
|     "sqlite3-cluster": "2.1.x", | ||||
|     "sqlite3-cluster": "git+https://git.daplie.com/coolaj86/sqlite3-cluster.git#v2", | ||||
|     "stripe": "^4.22.0", | ||||
|     "twilio": "1.x" | ||||
|     "twilio": "1.x", | ||||
|     "ursa": "^0.9.1" | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user