├── .env.example
├── .env.travis
├── .gitignore
├── .travis.yml
├── Vagrantfile
├── after.sh
├── aliases
├── app
├── Console
│ ├── Commands
│ │ ├── .gitkeep
│ │ └── CreateAuthenticationKeyCommand.php
│ └── Kernel.php
├── Events
│ ├── Event.php
│ └── ExampleEvent.php
├── Exceptions
│ └── Handler.php
├── Extensions
│ └── Authentication
│ │ └── PasetoAuthGuard.php
├── Http
│ ├── Controllers
│ │ ├── AuthController.php
│ │ └── Controller.php
│ ├── Middleware
│ │ ├── Authenticate.php
│ │ └── CorsMiddleware.php
│ └── Requests
│ │ ├── LoginRequest.php
│ │ └── RegisterRequest.php
├── Jobs
│ ├── ExampleJob.php
│ └── Job.php
├── Listeners
│ └── ExampleListener.php
├── Mail
│ ├── PasswordReset.php
│ └── Welcome.php
├── Providers
│ ├── AppServiceProvider.php
│ ├── AuthenticationProvider.php
│ └── EventServiceProvider.php
└── User.php
├── artisan
├── bootstrap
└── app.php
├── composer.json
├── composer.lock
├── config
├── apidoc.php
├── auth.php
├── constants.php
├── mail.php
└── permission.php
├── database
├── factories
│ └── ModelFactory.php
├── migrations
│ ├── .gitkeep
│ ├── 2018_01_01_000000_create_permission_tables.php
│ ├── 2018_03_03_232931_create_users_table.php
│ └── 2018_03_03_232948_create_password_resets_table.php
└── seeds
│ ├── DatabaseSeeder.php
│ └── RolesSeeder.php
├── helpers.php
├── phpunit-printer.yml
├── phpunit.xml
├── public
├── .htaccess
└── index.php
├── readme.md
├── resources
├── lang
│ └── en
│ │ ├── messages.php
│ │ └── validation.php
└── views
│ └── emails
│ ├── password-reset.blade.php
│ └── welcome.blade.php
├── routes
└── api.php
├── storage
├── app
│ └── .gitignore
├── clockwork
│ └── .gitignore
├── framework
│ ├── cache
│ │ └── .gitignore
│ └── views
│ │ └── .gitignore
└── logs
│ └── .gitignore
└── tests
├── AuthenticationTest.php
└── TestCase.php
/.env.example:
--------------------------------------------------------------------------------
1 | APP_ENV=local
2 | APP_DEBUG=true
3 | APP_KEY=
4 | APP_TIMEZONE=Europe/Berlin
5 |
6 | PASETO_AUTH_KEY=
7 | PASETO_AUTH_EXPIRE_AFTER_HOURS=12
8 | PASETO_AUTH_ISSUER=api
9 |
10 | LOG_CHANNEL=stack
11 | LOG_SLACK_WEBHOOK_URL=
12 |
13 | DB_CONNECTION=mysql
14 | DB_HOST=localhost
15 | DB_PORT=3306
16 | DB_DATABASE=homestead
17 | DB_USERNAME=homestead
18 | DB_PASSWORD=secret
19 |
20 | CACHE_DRIVER=file
21 | QUEUE_DRIVER=sync
22 |
23 | ALLOWED_ORIGIN=*
24 |
25 | APP_LOCALE=en
26 |
27 | WEBSITE_NAME=
28 | WEBSITE_EMAIL=
29 | FRONTEND_URL=
30 |
--------------------------------------------------------------------------------
/.env.travis:
--------------------------------------------------------------------------------
1 | APP_ENV=local
2 | APP_DEBUG=true
3 | APP_KEY=
4 | APP_TIMEZONE=Europe/Berlin
5 |
6 | PASETO_AUTH_KEY=
7 | PASETO_AUTH_EXPIRE_AFTER_HOURS=12
8 | PASETO_AUTH_ISSUER=api
9 |
10 | LOG_CHANNEL=stack
11 | LOG_SLACK_WEBHOOK_URL=
12 |
13 | DB_CONNECTION=mysql
14 | DB_HOST=localhost
15 | DB_PORT=3306
16 | DB_DATABASE=homestead
17 | DB_USERNAME=root
18 | DB_PASSWORD=
19 |
20 | CACHE_DRIVER=file
21 | QUEUE_DRIVER=sync
22 |
23 | ALLOWED_ORIGIN=*
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /.idea
3 | Homestead.json
4 | Homestead.yaml
5 | .env
6 | /database/database.sqlite
7 | /.vagrant/
8 | /.phpstorm.meta.php
9 | /_ide_helper.php
10 | /_ide_helper_models.php
11 | /public/docs/
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | dist: trusty
4 |
5 | php:
6 | - 7.1
7 | - 7.2
8 | - 7.3
9 | - 7.4snapshot
10 |
11 | branches:
12 | only:
13 | master
14 |
15 | sudo: true
16 |
17 | cache:
18 | directories:
19 | - $HOME/.composer/cache
20 |
21 | addons:
22 | apt:
23 | sources:
24 | - mysql-5.7-trusty
25 | packages:
26 | - mysql-server
27 | - mysql-client
28 |
29 | before_install:
30 | - mysql -e 'CREATE DATABASE IF NOT EXISTS homestead DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;'
31 |
32 | install:
33 | - travis_retry composer install --no-interaction --prefer-source
34 | - cp .env.travis .env
35 | - composer keys
36 | - php artisan migrate
37 |
38 |
39 | script: vendor/bin/phpunit
40 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | require 'json'
5 | require 'yaml'
6 |
7 | VAGRANTFILE_API_VERSION ||= "2"
8 | confDir = $confDir ||= File.expand_path("vendor/laravel/homestead", File.dirname(__FILE__))
9 |
10 | homesteadYamlPath = File.expand_path("Homestead.yaml", File.dirname(__FILE__))
11 | homesteadJsonPath = File.expand_path("Homestead.json", File.dirname(__FILE__))
12 | afterScriptPath = "after.sh"
13 | customizationScriptPath = "user-customizations.sh"
14 | aliasesPath = "aliases"
15 |
16 | require File.expand_path(confDir + '/scripts/homestead.rb')
17 |
18 | Vagrant.require_version '>= 1.9.0'
19 |
20 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
21 | if File.exist? aliasesPath then
22 | config.vm.provision "file", source: aliasesPath, destination: "/tmp/bash_aliases"
23 | config.vm.provision "shell" do |s|
24 | s.inline = "awk '{ sub(\"\r$\", \"\"); print }' /tmp/bash_aliases > /home/vagrant/.bash_aliases"
25 | end
26 | end
27 |
28 | if File.exist? homesteadYamlPath then
29 | settings = YAML::load(File.read(homesteadYamlPath))
30 | elsif File.exist? homesteadJsonPath then
31 | settings = JSON.parse(File.read(homesteadJsonPath))
32 | else
33 | abort "Homestead settings file not found in " + File.dirname(__FILE__)
34 | end
35 |
36 | Homestead.configure(config, settings)
37 |
38 | if File.exist? afterScriptPath then
39 | config.vm.provision "shell", path: afterScriptPath, privileged: false, keep_color: true
40 | end
41 |
42 | if File.exist? customizationScriptPath then
43 | config.vm.provision "shell", path: customizationScriptPath, privileged: false, keep_color: true
44 | end
45 |
46 | if defined? VagrantPlugins::HostsUpdater
47 | config.hostsupdater.aliases = settings['sites'].map { |site| site['map'] }
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/after.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # If you would like to do some extra provisioning you may
4 | # add any commands you wish to this file and they will
5 | # be run after the Homestead machine is provisioned.
6 |
--------------------------------------------------------------------------------
/aliases:
--------------------------------------------------------------------------------
1 | alias ..="cd .."
2 | alias ...="cd ../.."
3 |
4 | alias h='cd ~'
5 | alias c='clear'
6 | alias art=artisan
7 |
8 | alias phpspec='vendor/bin/phpspec'
9 | alias phpunit='vendor/bin/phpunit'
10 | alias serve=serve-laravel
11 |
12 | alias xoff='sudo phpdismod -s cli xdebug'
13 | alias xon='sudo phpenmod -s cli xdebug'
14 |
15 | function artisan() {
16 | php artisan "$@"
17 | }
18 |
19 | function dusk() {
20 | pids=$(pidof /usr/bin/Xvfb)
21 |
22 | if [ ! -n "$pids" ]; then
23 | Xvfb :0 -screen 0 1280x960x24 &
24 | fi
25 |
26 | php artisan dusk "$@"
27 | }
28 |
29 | function php56() {
30 | sudo update-alternatives --set php /usr/bin/php5.6
31 | }
32 |
33 | function php70() {
34 | sudo update-alternatives --set php /usr/bin/php7.0
35 | }
36 |
37 | function php71() {
38 | sudo update-alternatives --set php /usr/bin/php7.1
39 | }
40 |
41 | function php72() {
42 | sudo update-alternatives --set php /usr/bin/php7.2
43 | }
44 |
45 | function serve-apache() {
46 | if [[ "$1" && "$2" ]]
47 | then
48 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
49 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-apache.sh
50 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-apache.sh "$1" "$2" 80 443 "${3:-7.1}"
51 | else
52 | echo "Error: missing required parameters."
53 | echo "Usage: "
54 | echo " serve-apache domain path"
55 | fi
56 | }
57 |
58 | function serve-laravel() {
59 | if [[ "$1" && "$2" ]]
60 | then
61 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
62 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-laravel.sh
63 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-laravel.sh "$1" "$2" 80 443 "${3:-7.1}"
64 | else
65 | echo "Error: missing required parameters."
66 | echo "Usage: "
67 | echo " serve domain path"
68 | fi
69 | }
70 |
71 | function serve-proxy() {
72 | if [[ "$1" && "$2" ]]
73 | then
74 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-proxy.sh
75 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-proxy.sh "$1" "$2" 80 443 "${3:-7.1}"
76 | else
77 | echo "Error: missing required parameters."
78 | echo "Usage: "
79 | echo " serve-proxy domain port"
80 | fi
81 | }
82 |
83 | function serve-silverstripe() {
84 | if [[ "$1" && "$2" ]]
85 | then
86 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
87 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-silverstripe.sh
88 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-silverstripe.sh "$1" "$2" 80 443 "${3:-7.1}"
89 | else
90 | echo "Error: missing required parameters."
91 | echo "Usage: "
92 | echo " serve-silverstripe domain path"
93 | fi
94 | }
95 |
96 | function serve-spa() {
97 | if [[ "$1" && "$2" ]]
98 | then
99 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
100 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-spa.sh
101 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-spa.sh "$1" "$2" 80 443 "${3:-7.1}"
102 | else
103 | echo "Error: missing required parameters."
104 | echo "Usage: "
105 | echo " serve-spa domain path"
106 | fi
107 | }
108 |
109 | function serve-statamic() {
110 | if [[ "$1" && "$2" ]]
111 | then
112 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
113 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-statamic.sh
114 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-statamic.sh "$1" "$2" 80 443 "${3:-7.1}"
115 | else
116 | echo "Error: missing required parameters."
117 | echo "Usage: "
118 | echo " serve-statamic domain path"
119 | fi
120 | }
121 |
122 | function serve-symfony2() {
123 | if [[ "$1" && "$2" ]]
124 | then
125 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
126 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-symfony2.sh
127 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-symfony2.sh "$1" "$2" 80 443 "${3:-7.1}"
128 | else
129 | echo "Error: missing required parameters."
130 | echo "Usage: "
131 | echo " serve-symfony2 domain path"
132 | fi
133 | }
134 |
135 | function serve-symfony4() {
136 | if [[ "$1" && "$2" ]]
137 | then
138 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
139 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-symfony4.sh
140 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-symfony4.sh "$1" "$2" 80 443 "${3:-7.1}"
141 | else
142 | echo "Error: missing required parameters."
143 | echo "Usage: "
144 | echo " serve-symfony4 domain path"
145 | fi
146 | }
147 |
148 | function serve-pimcore() {
149 | if [[ "$1" && "$2" ]]
150 | then
151 | sudo bash /vagrant/vendor/laravel/homestead/scripts/create-certificate.sh "$1"
152 | sudo dos2unix /vagrant/vendor/laravel/homestead/scripts/serve-pimcore.sh
153 | sudo bash /vagrant/vendor/laravel/homestead/scripts/serve-pimcore.sh "$1" "$2" 80 443 "${3:-7.1}"
154 | else
155 | echo "Error: missing required parameters."
156 | echo "Usage: "
157 | echo " serve-pimcore domain path"
158 | fi
159 | }
160 |
161 | function share() {
162 | if [[ "$1" ]]
163 | then
164 | ngrok http ${@:2} -host-header="$1" 80
165 | else
166 | echo "Error: missing required parameters."
167 | echo "Usage: "
168 | echo " share domain"
169 | echo "Invocation with extra params passed directly to ngrok"
170 | echo " share domain -region=eu -subdomain=test1234"
171 | fi
172 | }
173 |
174 | function flip() {
175 | sudo bash /vagrant/vendor/laravel/homestead/scripts/flip-webserver.sh
176 | }
177 |
178 | function __has_pv() {
179 | $(hash pv 2>/dev/null);
180 |
181 | return $?
182 | }
183 |
184 | function __pv_install_message() {
185 | if ! __has_pv; then
186 | echo $1
187 | echo "Install pv with \`sudo apt-get install -y pv\` then run this command again."
188 | echo ""
189 | fi
190 | }
191 |
192 | function dbexport() {
193 | FILE=${1:-/vagrant/mysqldump.sql.gz}
194 |
195 | # This gives an estimate of the size of the SQL file
196 | # It appears that 80% is a good approximation of
197 | # the ratio of estimated size to actual size
198 | SIZE_QUERY="select ceil(sum(data_length) * 0.8) as size from information_schema.TABLES"
199 |
200 | __pv_install_message "Want to see export progress?"
201 |
202 | echo "Exporting databases to '$FILE'"
203 |
204 | if __has_pv; then
205 | ADJUSTED_SIZE=$(mysql --vertical -uhomestead -psecret -e "$SIZE_QUERY" 2>/dev/null | grep 'size' | awk '{print $2}')
206 | HUMAN_READABLE_SIZE=$(numfmt --to=iec-i --suffix=B --format="%.3f" $ADJUSTED_SIZE)
207 |
208 | echo "Estimated uncompressed size: $HUMAN_READABLE_SIZE"
209 | mysqldump -uhomestead -psecret --all-databases --skip-lock-tables 2>/dev/null | pv --size=$ADJUSTED_SIZE | gzip > "$FILE"
210 | else
211 | mysqldump -uhomestead -psecret --all-databases --skip-lock-tables 2>/dev/null | gzip > "$FILE"
212 | fi
213 |
214 | echo "Done."
215 | }
216 |
217 | function dbimport() {
218 | FILE=${1:-/vagrant/mysqldump.sql.gz}
219 |
220 | __pv_install_message "Want to see import progress?"
221 |
222 | echo "Importing databases from '$FILE'"
223 |
224 | if __has_pv; then
225 | pv "$FILE" --progress --eta | zcat | mysql -uhomestead -psecret 2>/dev/null
226 | else
227 | cat "$FILE" | zcat | mysql -uhomestead -psecret 2>/dev/null
228 | fi
229 |
230 | echo "Done."
231 | }
232 |
233 | function xphp() {
234 | (php -m | grep -q xdebug)
235 | if [[ $? -eq 0 ]]
236 | then
237 | XDEBUG_ENABLED=true
238 | else
239 | XDEBUG_ENABLED=false
240 | fi
241 |
242 | if ! $XDEBUG_ENABLED; then xon; fi
243 |
244 | php \
245 | -dxdebug.remote_host=192.168.10.1 \
246 | -dxdebug.remote_autostart=1 \
247 | "$@"
248 |
249 | if ! $XDEBUG_ENABLED; then xoff; fi
250 | }
251 |
--------------------------------------------------------------------------------
/app/Console/Commands/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mstaack/lumen-api-starter/c22cc5afe669609b744045440e37f0303d3f98b3/app/Console/Commands/.gitkeep
--------------------------------------------------------------------------------
/app/Console/Commands/CreateAuthenticationKeyCommand.php:
--------------------------------------------------------------------------------
1 | encode();
33 |
34 | if (file_exists($envFilePath = $this->getPathToEnvFile()) === false) {
35 | $this->info("Could not find env file! Key: $key");
36 | }
37 |
38 | if ($this->updateEnvFile($envFilePath, $key)) {
39 | $this->info("File .env updated with key: $key");
40 | }
41 | }
42 |
43 | /**
44 | * @return string
45 | */
46 | private function getPathToEnvFile()
47 | {
48 | return base_path('.env');
49 | }
50 |
51 | private function updateEnvFile($path, $key)
52 | {
53 | if (file_exists($path)) {
54 |
55 | $oldContent = file_get_contents($path);
56 | $search = 'PASETO_AUTH_KEY=' . env('PASETO_AUTH_KEY');
57 |
58 | if (!str_contains($oldContent, $search)) {
59 | $search = 'PASETO_AUTH_KEY=';
60 | }
61 |
62 | $newContent = str_replace($search, 'PASETO_AUTH_KEY=' . $key, $oldContent);
63 |
64 | return file_put_contents($path, $newContent);
65 | }
66 |
67 | return false;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | getResponse();
54 | }
55 |
56 | // AuthorizationException
57 | if ($e instanceof AuthorizationException) {
58 | return response()->json([
59 | 'errors' => [
60 | 'title' => 'Unauthorized'
61 | ]
62 | ], 401);
63 | }
64 |
65 | //Http Exceptions
66 | if ($e instanceof HttpException) {
67 | $response['message'] = $e->getMessage() ?: Response::$statusTexts[$e->getStatusCode()];
68 | $response['status'] = $e->getStatusCode();
69 |
70 | return response()->json($response, $response['status']);
71 | }
72 |
73 | //Default Exception Response
74 | $response = [
75 | 'message' => 'Whoops! Something went wrong.',
76 | 'status' => 500
77 | ];
78 |
79 | if ($this->isDebugMode()) {
80 | $response['debug'] = [
81 | 'message' => $e->getMessage(),
82 | 'exception' => get_class($e),
83 | 'code' => $e->getCode(),
84 | 'file' => $e->getFile(),
85 | 'line' => $e->getLine(),
86 | ];
87 |
88 | //clean trace
89 | foreach ($e->getTrace() as $item) {
90 | if (isset($item['args']) && is_array($item['args'])) {
91 | $item['args'] = $this->cleanTraceArgs($item['args']);
92 | }
93 | $response['debug']['trace'][] = $item;
94 | }
95 | }
96 |
97 | return response()->json($response, $response['status']);
98 | }
99 | /**
100 | * Determine if the application is in debug mode.
101 | *
102 | * @return Boolean
103 | */
104 | public function isDebugMode()
105 | {
106 | return (boolean)env('APP_DEBUG');
107 | }
108 | /**
109 | * @param array $args
110 | * @param int $level
111 | * @param int $count
112 | *
113 | * @return array|string
114 | */
115 | private function cleanTraceArgs(array $args, $level = 0, &$count = 0)
116 | {
117 | $result = [];
118 | foreach ($args as $key => $value) {
119 | if (++$count > 1e4) {
120 | return '*SKIPPED over 10000 entries*';
121 | }
122 | if (is_object($value)) {
123 | $result[$key] = get_class($value);
124 | } elseif (is_array($value)) {
125 | if ($level > 10) {
126 | $result[$key] = '*DEEP NESTED ARRAY*';
127 | } else {
128 | $result[$key] = $this->cleanTraceArgs($value, $level + 1, $count);
129 | }
130 | } elseif (is_null($value)) {
131 | $result[$key] = null;
132 | } elseif (is_bool($value)) {
133 | $result[$key] = $value;
134 | } elseif (is_int($value)) {
135 | $result[$key] = $value;
136 | } elseif (is_resource($value)) {
137 | $result[$key] = get_resource_type($value);
138 | } elseif ($value instanceof \__PHP_Incomplete_Class) {
139 | $array = new \ArrayObject($value);
140 | $result[$key] = $array['__PHP_Incomplete_Class_Name'];
141 | } elseif (is_string($value) && mb_detect_encoding($value) === false) {
142 | $result[$key] = 'REMOVED-BINARY-BLOB';
143 | } else {
144 | $result[$key] = (string)$value;
145 | }
146 | }
147 | return $result;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/app/Extensions/Authentication/PasetoAuthGuard.php:
--------------------------------------------------------------------------------
1 | provider = $provider;
54 | $this->request = $request;
55 | }
56 |
57 | /**
58 | * Determine if the current request is a guest user.
59 | *
60 | * @return bool
61 | * @throws TypeError
62 | * @throws InvalidVersionException
63 | * @throws PasetoException
64 | */
65 | public function guest()
66 | {
67 | if ($this->user !== null) {
68 | return false;
69 | }
70 |
71 | return !$this->check();
72 | }
73 |
74 | /**
75 | * Determine if the current user is authenticated.
76 | *
77 | * @return bool
78 | * @throws TypeError
79 | * @throws InvalidVersionException
80 | * @throws PasetoException
81 | */
82 | public function check()
83 | {
84 | $parser = Parser::getLocal($this->getSharedKey(), ProtocolCollection::v2());
85 |
86 | $parser->addRule(new NotExpired);
87 | $parser->addRule(new IssuedBy($this->getIssuer()));
88 |
89 | try {
90 | $this->token = $parser->parse($this->getTokenFromRequest());
91 | } catch (PasetoException $e) {
92 | return false;
93 | }
94 |
95 | return true;
96 | }
97 |
98 | /**
99 | * @return SymmetricKey
100 | * @throws TypeError
101 | */
102 | private function getSharedKey()
103 | {
104 | return SymmetricKey::fromEncodedString(env('PASETO_AUTH_KEY'));
105 | }
106 |
107 | /**
108 | * @return mixed
109 | */
110 | private function getIssuer()
111 | {
112 | return env('PASETO_AUTH_ISSUER');
113 | }
114 |
115 | /**
116 | * @return string|bool
117 | */
118 | private function getTokenFromRequest()
119 | {
120 | if ($token = $this->request->headers->get('Authorization')) {
121 | return str_after($token, 'Bearer ');
122 | }
123 |
124 | return false;
125 | }
126 |
127 | /**
128 | * Attempt to authenticate the user using the given credentials and return the token.
129 | *
130 | * @param array $credentials
131 | *
132 | * @return mixed
133 | * @throws Exception
134 | * @throws InvalidKeyException
135 | * @throws InvalidPurposeException
136 | * @throws PasetoException
137 | * @throws TypeError
138 | */
139 | public function attempt(array $credentials = [])
140 | {
141 | $user = $this->provider->retrieveByCredentials($credentials);
142 |
143 | if ($user && $user->verified && $this->provider->validateCredentials($user, $credentials)) {
144 | return $this->login($user);
145 | }
146 |
147 | return false;
148 | }
149 |
150 | /**
151 | * @param $user
152 | * @return string
153 | * @throws TypeError
154 | * @throws Exception
155 | * @throws InvalidKeyException
156 | * @throws PasetoException
157 | * @throws InvalidPurposeException
158 | */
159 | private function login($user)
160 | {
161 | $this->setUser($user);
162 |
163 | return $this->generateTokenForUser();
164 | }
165 |
166 | /**
167 | * Set the current user.
168 | *
169 | * @param Authenticatable $user
170 | * @return $this
171 | */
172 | public function setUser(Authenticatable $user)
173 | {
174 | $this->user = $user;
175 |
176 | return $this;
177 | }
178 |
179 | /**
180 | * @return string
181 | * @throws InvalidKeyException
182 | * @throws InvalidPurposeException
183 | * @throws PasetoException
184 | * @throws TypeError
185 | */
186 | private function generateTokenForUser()
187 | {
188 | $claims = [
189 | 'id' => $this->user->id
190 | ];
191 |
192 | $token = $this->getTokenBuilder()
193 | ->setExpiration(Carbon::now()->addHours($this->getExpireTime()))
194 | ->setIssuer($this->getIssuer())
195 | ->setClaims($claims);
196 |
197 | return (string)$token;
198 | }
199 |
200 | /**
201 | * @return Builder
202 | * @throws PasetoException
203 | * @throws TypeError
204 | * @throws InvalidKeyException
205 | * @throws InvalidPurposeException
206 | */
207 | private function getTokenBuilder()
208 | {
209 | return (new Builder)
210 | ->setKey($this->getSharedKey())
211 | ->setVersion(new Version2)
212 | ->setPurpose(Purpose::local());
213 | }
214 |
215 | /**
216 | * @return mixed
217 | */
218 | private function getExpireTime()
219 | {
220 | return env('PASETO_AUTH_EXPIRE_AFTER_HOURS');
221 | }
222 |
223 | /**
224 | * Get the currently authenticated user.
225 | *
226 | * @return \Illuminate\Contracts\Auth\Authenticatable|null
227 | * @throws PasetoException
228 | */
229 | public function user()
230 | {
231 | if (!$this->user && $this->token) {
232 | $this->user = $this->provider->retrieveById($this->token->get('id'));
233 | }
234 |
235 | return $this->user;
236 | }
237 |
238 | /**
239 | * Get the ID for the currently authenticated user.
240 | *
241 | * @return int|null
242 | */
243 | public function id()
244 | {
245 | return $this->user->id;
246 | }
247 |
248 | /**
249 | * Validate a user's credentials.
250 | *
251 | * @param array $credentials
252 | * @return bool
253 | */
254 | public function validate(array $credentials = [])
255 | {
256 | return $this->provider->validateCredentials($this->user, $credentials);
257 | }
258 |
259 | /**
260 | * Set the current request instance.
261 | *
262 | * @param \Illuminate\Http\Request $request
263 | * @return $this
264 | */
265 | public function setRequest(Request $request)
266 | {
267 | $this->request = $request;
268 |
269 | return $this;
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/app/Http/Controllers/AuthController.php:
--------------------------------------------------------------------------------
1 | json(['data' => Auth::user()]);
35 | }
36 |
37 | /**
38 | * Login
39 | *
40 | * @bodyParam email string required The email
41 | * @bodyParam password string required The password
42 | *
43 | * @param LoginRequest $request
44 | * @return JsonResponse
45 | */
46 | public function login(LoginRequest $request)
47 | {
48 | $credentials = $request->only('email', 'password');
49 |
50 | $token = Auth::attempt($credentials);
51 |
52 | if (!$token) {
53 | return response()->json(['message' => trans('messages.login_failed')], 401);
54 | }
55 |
56 | return response()->json(['data' => ['user' => Auth::user(), 'token' => $token]]);
57 | }
58 |
59 | /**
60 | * Register
61 | *
62 | * @bodyParam email string required The email
63 | * @bodyParam password string required The password
64 | * @bodyParam name string required The name
65 | *
66 | * @param RegisterRequest $request
67 | * @return JsonResponse
68 | */
69 | public function register(RegisterRequest $request)
70 | {
71 | $email = $request->input('email');
72 | $password = $request->input('password');
73 | $name = $request->input('name');
74 |
75 | $user = User::createFromValues($name, $email, $password);
76 |
77 | Mail::to($user)->send(new Welcome($user));
78 |
79 | return response()->json(['data' => ['message' => 'Account created. Please verify via email.']]);
80 | }
81 |
82 | /**
83 | * Verify User
84 | *
85 | * @queryParam token required The token
86 | *
87 | * @param String $token
88 | * @return JsonResponse
89 | * @throws Exception
90 | */
91 | public function verify($token)
92 | {
93 | $user = User::verifyByToken($token);
94 |
95 | if (!$user) {
96 | return response()->json(['data' => ['message' => 'Invalid verification token']], 400);
97 | }
98 |
99 | return response()->json(['data' => ['message' => 'Account has been verified']]);
100 | }
101 |
102 | /**
103 | * Send new Password Request
104 | *
105 | * @bodyParam email string required The email
106 | *
107 | * @param Request $request
108 | * @return JsonResponse
109 | */
110 | public function forgotPassword(Request $request)
111 | {
112 | $validator = Validator::make($request->all(), [
113 | 'email' => 'required|exists:users,email'
114 | ]);
115 |
116 | if ($validator->passes()) {
117 | $user = User::byEmail($request->input('email'));
118 |
119 | Mail::to($user)->send(new PasswordReset($user));
120 | }
121 |
122 | return response()->json(['data' => ['message' => 'Please check your email to reset your password.']]);
123 | }
124 |
125 | /**
126 | * Create new P assword
127 | *
128 | * @bodyParam password string required The new password
129 | *
130 | * @param Request $request
131 | * @param $token
132 | * @return JsonResponse
133 | * @throws ValidationException
134 | */
135 | public function recoverPassword(Request $request, $token)
136 | {
137 | $this->validate($request, [
138 | 'password' => 'required|min:8',
139 | ]);
140 |
141 | $user = User::newPasswordByResetToken($token, $request->input('password'));
142 |
143 | if ($user) {
144 | return response()->json(['data' => ['message' => 'Password has been changed.']]);
145 | } else {
146 | return response()->json(['data' => ['message' => 'Invalid password reset token']], 400);
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | auth = $auth;
30 | }
31 |
32 | /**
33 | * Handle an incoming request.
34 | *
35 | * @param \Illuminate\Http\Request $request
36 | * @param \Closure $next
37 | * @param string|null $guard
38 | * @return mixed
39 | */
40 | public function handle($request, Closure $next, $guard = null)
41 | {
42 | if ($this->auth->guard($guard)->guest()) {
43 | return response()->json([
44 | 'errors' => [
45 | 'title' => 'Unauthorized'
46 | ]
47 | ], 401);
48 | }
49 |
50 | return $next($request);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Http/Middleware/CorsMiddleware.php:
--------------------------------------------------------------------------------
1 | json(['data' => ['message' => 'No valid cors settings found!']], 401);
30 | }
31 |
32 | $headers = [
33 | 'Access-Control-Allow-Origin' => $allowedOrigin,
34 | 'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, PUT, DELETE',
35 | 'Access-Control-Allow-Credentials' => 'true',
36 | 'Access-Control-Max-Age' => '86400',
37 | 'Access-Control-Allow-Headers' => $this->getAllowedHeaders()
38 | ];
39 |
40 | if ($request->isMethod('OPTIONS')) {
41 | return response()->json('{"method":"OPTIONS"}', 200, $headers);
42 | }
43 |
44 | $response = $next($request);
45 |
46 | foreach ($headers as $key => $value) {
47 | if (method_exists($response, 'header')) {
48 | $response->header($key, $value);
49 | }
50 | }
51 |
52 | return $response;
53 | }
54 |
55 | /**
56 | * @return string
57 | */
58 | private function getAllowedHeaders()
59 | {
60 | return implode(', ', $this->allowedHeaders);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/Http/Requests/LoginRequest.php:
--------------------------------------------------------------------------------
1 | merge(['email' => strtoLower($this->input('email'))]);
15 | }
16 |
17 | /**
18 | * Determine if the user is authorized to make this request.
19 | *
20 | * @return bool
21 | */
22 | public function authorize()
23 | {
24 | return true;
25 | }
26 |
27 | /**
28 | * Get the validation rules that apply to the request.
29 | *
30 | * @return array
31 | */
32 | public function rules()
33 | {
34 | return [
35 | 'email' => 'required',
36 | 'password' => 'required',
37 | ];
38 | }
39 |
40 | /**
41 | * Get custom messages for validator errors.
42 | *
43 | * @return array
44 | */
45 | public function messages()
46 | {
47 | return [
48 | //
49 | ];
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/Http/Requests/RegisterRequest.php:
--------------------------------------------------------------------------------
1 | merge(['email' => strtoLower($this->input('email'))]);
15 | }
16 |
17 | /**
18 | * Determine if the user is authorized to make this request.
19 | *
20 | * @return bool
21 | */
22 | public function authorize()
23 | {
24 | return true;
25 | }
26 |
27 | /**
28 | * Get the validation rules that apply to the request.
29 | *
30 | * @return array
31 | */
32 | public function rules()
33 | {
34 | return [
35 | 'email' => 'required|unique:users,email',
36 | 'password' => 'required',
37 | 'name' => 'string'
38 | ];
39 | }
40 |
41 | /**
42 | * Get custom messages for validator errors.
43 | *
44 | * @return array
45 | */
46 | public function messages()
47 | {
48 | return [
49 | //
50 | ];
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Jobs/ExampleJob.php:
--------------------------------------------------------------------------------
1 | user = $user;
27 | }
28 |
29 | /**
30 | * Build the message.
31 | *
32 | * @return $this
33 | */
34 | public function build()
35 | {
36 | return $this
37 | ->subject(trans('messages.password_reset_subject'))
38 | ->view('emails.password-reset')
39 | ->with(['name' => $this->user->name, 'token' => $this->user->createPasswordRecoveryToken()]);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Mail/Welcome.php:
--------------------------------------------------------------------------------
1 | user = $user;
27 | }
28 |
29 | /**
30 | * Build the message.
31 | *
32 | * @return $this
33 | */
34 | public function build()
35 | {
36 | return $this
37 | ->subject(trans('messages.welcome_subject'))
38 | ->view('emails.welcome')
39 | ->with(['name' => $this->user->name, 'token' => $this->user->verification_token]);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | app['auth']->extend('paseto', function ($app, $name, array $config) {
26 |
27 | $guard = new PasetoAuthGuard(
28 | $app['auth']->createUserProvider($config['provider']),
29 | $app['request']
30 | );
31 |
32 | $app->refresh('request', $guard, 'setRequest');
33 |
34 | return $guard;
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/Providers/EventServiceProvider.php:
--------------------------------------------------------------------------------
1 | [
16 | 'App\Listeners\EventListener',
17 | ],
18 | ];
19 | }
20 |
--------------------------------------------------------------------------------
/app/User.php:
--------------------------------------------------------------------------------
1 | roles->pluck('name');
56 | }
57 |
58 | /**
59 | * Create a user
60 | *
61 | * @param $name
62 | * @param $email
63 | * @param $password
64 | * @return User|bool
65 | */
66 | public static function createFromValues($name, $email, $password)
67 | {
68 | $user = new static;
69 |
70 | $user->name = $name;
71 | $user->email = $email;
72 | $user->password = Hash::make($password);
73 | $user->verification_token = Str::random(64);
74 |
75 | return $user->save() ? $user : false;
76 | }
77 |
78 | /**
79 | * Get user by email
80 | *
81 | * @param $email
82 | * @return User
83 | */
84 | public static function byEmail($email)
85 | {
86 | return (new static)->where(compact('email'))->first();
87 | }
88 |
89 | /**
90 | * Verify by token
91 | *
92 | * @param $token
93 | * @return false|User
94 | */
95 | public static function verifyByToken($token)
96 | {
97 | $user = (new static)->where(['verification_token' => $token, 'verified' => 0])->first();
98 |
99 | if (!$user) {
100 | return false;
101 | }
102 |
103 | $user->verify();
104 |
105 | return $user;
106 | }
107 |
108 | /**
109 | * Verifiy a user
110 | *
111 | * @return bool
112 | */
113 | public function verify()
114 | {
115 | $this->verification_token = null;
116 | $this->verified = 1;
117 |
118 | return $this->save();
119 | }
120 |
121 | /**
122 | * Create password recovery token
123 | */
124 | public function createPasswordRecoveryToken()
125 | {
126 | $token = Str::random(64);
127 |
128 | $created = DB::table('password_resets')->updateOrInsert(
129 | ['email' => $this->email],
130 | ['email' => $this->email, 'token' => $token]
131 | );
132 |
133 | return $created ? $token : false;
134 | }
135 |
136 | /**
137 | * Restore password by token
138 | *
139 | * @param $token
140 | * @param $password
141 | * @return false|User
142 | */
143 | public static function newPasswordByResetToken($token, $password)
144 | {
145 | $query = DB::table('password_resets')->where(compact('token'));
146 | $record = $query->first();
147 |
148 | if (!$record) {
149 | return false;
150 | }
151 |
152 | $user = self::byEmail($record->email);
153 |
154 | $query->delete();
155 |
156 | return $user->setPassword($password);
157 | }
158 |
159 | /**
160 | * Persist a new password for the user
161 | *
162 | * @param $password
163 | * @return bool
164 | */
165 | public function setPassword($password)
166 | {
167 | $this->password = Hash::make($password);
168 | return $this->save();
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(
32 | 'Illuminate\Contracts\Console\Kernel'
33 | );
34 |
35 | exit($kernel->handle(new ArgvInput, new ConsoleOutput));
36 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | bootstrap();
8 |
9 | /*
10 | |--------------------------------------------------------------------------
11 | | Create The Application
12 | |--------------------------------------------------------------------------
13 | |
14 | | Here we will load the environment and create the application instance
15 | | that serves as the central piece of this framework. We'll use this
16 | | application as an "IoC" container and router for this framework.
17 | |
18 | */
19 |
20 | $app = new Laravel\Lumen\Application(
21 | realpath(__DIR__ . '/../')
22 | );
23 |
24 | $app->withFacades();
25 |
26 | $app->withEloquent();
27 |
28 | $app->alias('cache', 'Illuminate\Cache\CacheManager');
29 |
30 | /*
31 | |--------------------------------------------------------------------------
32 | | Register Configs
33 | |--------------------------------------------------------------------------
34 | |
35 | | Lumen uses a simpler way to load config variables.
36 | |
37 | */
38 | $app->configure('mail');
39 | $app->configure('permission');
40 | $app->configure('constants');
41 | $app->configure('apidoc');
42 |
43 | /*
44 | |--------------------------------------------------------------------------
45 | | Register Container Bindings
46 | |--------------------------------------------------------------------------
47 | |
48 | | Now we will register a few bindings in the service container. We will
49 | | register the exception handler and the console kernel. You may add
50 | | your own bindings here if you like or you can make another file.
51 | |
52 | */
53 |
54 | $app->singleton(
55 | Illuminate\Contracts\Debug\ExceptionHandler::class,
56 | App\Exceptions\Handler::class
57 | );
58 |
59 | $app->singleton(
60 | Illuminate\Contracts\Console\Kernel::class,
61 | App\Console\Kernel::class
62 | );
63 |
64 | /*
65 | |--------------------------------------------------------------------------
66 | | Register Middleware
67 | |--------------------------------------------------------------------------
68 | |
69 | | Next, we will register the middleware with the application. These can
70 | | be global middleware that run before and after each request into a
71 | | route or middleware that'll be assigned to some specific routes.
72 | |
73 | */
74 |
75 | $app->middleware([
76 | \App\Http\Middleware\CorsMiddleware::class
77 | ]);
78 |
79 | $app->routeMiddleware([
80 | 'auth' => App\Http\Middleware\Authenticate::class,
81 | 'permission' => Spatie\Permission\Middlewares\PermissionMiddleware::class,
82 | 'role' => Spatie\Permission\Middlewares\RoleMiddleware::class,
83 | ]);
84 |
85 | /*
86 | |--------------------------------------------------------------------------
87 | | Register Service Providers
88 | |--------------------------------------------------------------------------
89 | |
90 | | Here we will register all of the application's service providers which
91 | | are used to bind services into the container. Service providers are
92 | | totally optional, so you are not required to uncomment this line.
93 | |
94 | */
95 |
96 | $app->register(\App\Providers\AuthenticationProvider::class);
97 | $app->register(Pearl\RequestValidate\RequestServiceProvider::class);
98 | $app->register(\BeyondCode\DumpServer\DumpServerServiceProvider::class);
99 | $app->register(\Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);
100 | $app->register(Clockwork\Support\Lumen\ClockworkServiceProvider::class);
101 | $app->register(\Illuminate\Mail\MailServiceProvider::class);
102 | $app->register(Spatie\Permission\PermissionServiceProvider::class);
103 |
104 | if ($app->environment() !== 'production') {
105 | $app->register(\Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class);
106 | $app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
107 | }
108 |
109 | // $app->register(App\Providers\AppServiceProvider::class);
110 | // $app->register(App\Providers\EventServiceProvider::class);
111 |
112 | /*
113 | |--------------------------------------------------------------------------
114 | | Load The Application Routes
115 | |--------------------------------------------------------------------------
116 | |
117 | | Next we will include the routes file so that they can all be added to
118 | | the application. This will provide all of the URLs the application
119 | | can respond to, as well as the controllers that may handle them.
120 | |
121 | */
122 |
123 | $app->router->group([
124 | 'namespace' => 'App\Http\Controllers',
125 | ], function ($router) {
126 | require __DIR__ . '/../routes/api.php';
127 | });
128 |
129 | return $app;
130 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mstaack/lumen-api-starter",
3 | "description": "Lumen Starter for APIs",
4 | "keywords": [
5 | "framework",
6 | "laravel",
7 | "lumen",
8 | "json",
9 | "rest",
10 | "api"
11 | ],
12 | "license": "MIT",
13 | "type": "project",
14 | "require": {
15 | "php": ">=7.1.3",
16 | "laravel/lumen-framework": "5.8.*",
17 | "vlucas/phpdotenv": "^3.3",
18 | "flipbox/lumen-generator": "^5.6",
19 | "paragonie/paseto": "^0.5.0",
20 | "itsgoingd/clockwork": "^2.2",
21 | "pearl/lumen-request-validate": "^1.0",
22 | "illuminate/mail": "^5.7",
23 | "spatie/laravel-permission": "^2.25",
24 | "beyondcode/laravel-dump-server": "^1.1",
25 | "mpociot/laravel-apidoc-generator": "^3.4"
26 | },
27 | "require-dev": {
28 | "fzaninotto/faker": "~1.4",
29 | "phpunit/phpunit": "~7.0",
30 | "mockery/mockery": "~1.0",
31 | "barryvdh/laravel-ide-helper": "^2.4",
32 | "nunomaduro/collision": "^2.0",
33 | "codedungeon/phpunit-result-printer": "^0.19.10",
34 | "laravel/homestead": "^7.19"
35 | },
36 | "autoload": {
37 | "psr-4": {
38 | "App\\": "app/"
39 | },
40 | "files": [
41 | "helpers.php"
42 | ]
43 | },
44 | "autoload-dev": {
45 | "classmap": [
46 | "tests/",
47 | "database/"
48 | ]
49 | },
50 | "scripts": {
51 | "post-create-project-cmd": [
52 | "php -r \"copy('.env.example', '.env');\"",
53 | "php artisan key:generate"
54 | ],
55 | "post-root-package-install": [
56 | "php vendor/bin/homestead make",
57 | "composer keys",
58 | "composer meta"
59 | ],
60 | "meta": [
61 | "php artisan ide-helper:generate",
62 | "php artisan ide-helper:meta",
63 | "php artisan ide-helper:model",
64 | "php artisan optimize"
65 | ],
66 | "keys": [
67 | "php artisan key:generate",
68 | "php artisan auth:generate-paseto-key"
69 | ]
70 | },
71 | "minimum-stability": "dev",
72 | "prefer-stable": true,
73 | "config": {
74 | "optimize-autoloader": true
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/config/apidoc.php:
--------------------------------------------------------------------------------
1 | 'public/docs',
9 |
10 | /*
11 | * The router to be used (Laravel or Dingo).
12 | */
13 | 'router' => 'laravel',
14 |
15 | /*
16 | * Generate a Postman collection in addition to HTML docs.
17 | */
18 | 'postman' => [
19 | /*
20 | * Specify whether the Postman collection should be generated.
21 | */
22 | 'enabled' => true,
23 |
24 | /*
25 | * The name for the exported Postman collection. Default: config('app.name')." API"
26 | */
27 | 'name' => null,
28 |
29 | /*
30 | * The description for the exported Postman collection.
31 | */
32 | 'description' => null,
33 | ],
34 |
35 | /*
36 | * The routes for which documentation should be generated.
37 | * Each group contains rules defining which routes should be included ('match', 'include' and 'exclude' sections)
38 | * and rules which should be applied to them ('apply' section).
39 | */
40 | 'routes' => [
41 | [
42 | /*
43 | * Specify conditions to determine what routes will be parsed in this group.
44 | * A route must fulfill ALL conditions to pass.
45 | */
46 | 'match' => [
47 |
48 | /*
49 | * Match only routes whose domains match this pattern (use * as a wildcard to match any characters).
50 | */
51 | 'domains' => [
52 | '*',
53 | // 'domain1.*',
54 | ],
55 |
56 | /*
57 | * Match only routes whose paths match this pattern (use * as a wildcard to match any characters).
58 | */
59 | 'prefixes' => [
60 | '*',
61 | // 'users/*',
62 | ],
63 |
64 | /*
65 | * Match only routes registered under this version. This option is ignored for Laravel router.
66 | * Note that wildcards are not supported.
67 | */
68 | 'versions' => [
69 | 'v1',
70 | ],
71 | ],
72 |
73 | /*
74 | * Include these routes when generating documentation,
75 | * even if they did not match the rules above.
76 | * Note that the route must be referenced by name here (wildcards are supported).
77 | */
78 | 'include' => [
79 | // 'users.index', 'healthcheck*'
80 | ],
81 |
82 | /*
83 | * Exclude these routes when generating documentation,
84 | * even if they matched the rules above.
85 | * Note that the route must be referenced by name here (wildcards are supported).
86 | */
87 | 'exclude' => [
88 | // 'users.create', 'admin.*'
89 | ],
90 |
91 | /*
92 | * Specify rules to be applied to all the routes in this group when generating documentation
93 | */
94 | 'apply' => [
95 | /*
96 | * Specify headers to be added to the example requests
97 | */
98 | 'headers' => [
99 | // 'Authorization' => 'Bearer {token}',
100 | // 'Api-Version' => 'v2',
101 | ],
102 |
103 | /*
104 | * If no @response or @transformer declarations are found for the route,
105 | * we'll try to get a sample response by attempting an API call.
106 | * Configure the settings for the API call here,
107 | */
108 | 'response_calls' => [
109 | /*
110 | * API calls will be made only for routes in this group matching these HTTP methods (GET, POST, etc).
111 | * List the methods here or use '*' to mean all methods. Leave empty to disable API calls.
112 | */
113 | 'methods' => ['GET'],
114 |
115 | /*
116 | * For URLs which have parameters (/users/{user}, /orders/{id?}),
117 | * specify what values the parameters should be replaced with.
118 | * Note that you must specify the full parameter, including curly brackets and question marks if any.
119 | */
120 | 'bindings' => [
121 | // '{user}' => 1
122 | ],
123 |
124 | /*
125 | * Environment variables which should be set for the API call.
126 | * This is a good place to ensure that notifications, emails
127 | * and other external services are not triggered during the documentation API calls
128 | */
129 | 'env' => [
130 | 'APP_ENV' => 'documentation',
131 | 'APP_DEBUG' => false,
132 | // 'env_var' => 'value',
133 | ],
134 |
135 | /*
136 | * Headers which should be sent with the API call.
137 | */
138 | 'headers' => [
139 | 'Content-Type' => 'application/json',
140 | 'Accept' => 'application/json',
141 | // 'key' => 'value',
142 | ],
143 |
144 | /*
145 | * Cookies which should be sent with the API call.
146 | */
147 | 'cookies' => [
148 | // 'name' => 'value'
149 | ],
150 |
151 | /*
152 | * Query parameters which should be sent with the API call.
153 | */
154 | 'query' => [
155 | // 'key' => 'value',
156 | ],
157 |
158 | /*
159 | * Body parameters which should be sent with the API call.
160 | */
161 | 'body' => [
162 | // 'key' => 'value',
163 | ],
164 | ],
165 | ],
166 | ],
167 | ],
168 |
169 | /*
170 | * Custom logo path. Will be copied during generate command. Set this to false to use the default logo.
171 | *
172 | * Change to an absolute path to use your custom logo. For example:
173 | * 'logo' => resource_path('views') . '/api/logo.png'
174 | *
175 | * If you want to use this, please be aware of the following rules:
176 | * - size: 230 x 52
177 | */
178 | 'logo' => false,
179 |
180 | /*
181 | * Configure how responses are transformed using @transformer and @transformerCollection
182 | * Requires league/fractal package: composer require league/fractal
183 | *
184 | * If you are using a custom serializer with league/fractal,
185 | * you can specify it here.
186 | *
187 | * Serializers included with league/fractal:
188 | * - \League\Fractal\Serializer\ArraySerializer::class
189 | * - \League\Fractal\Serializer\DataArraySerializer::class
190 | * - \League\Fractal\Serializer\JsonApiSerializer::class
191 | *
192 | * Leave as null to use no serializer or return a simple JSON.
193 | */
194 | 'fractal' => [
195 | 'serializer' => null,
196 | ],
197 | ];
198 |
--------------------------------------------------------------------------------
/config/auth.php:
--------------------------------------------------------------------------------
1 | [
17 | 'guard' => env('AUTH_GUARD', 'api'),
18 | ],
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Authentication Guards
23 | |--------------------------------------------------------------------------
24 | |
25 | | Next, you may define every authentication guard for your application.
26 | | Of course, a great default configuration has been defined for you
27 | | here which uses session storage and the Eloquent user provider.
28 | |
29 | | All authentication drivers have a user provider. This defines how the
30 | | users are actually retrieved out of your database or other storage
31 | | mechanisms used by this application to persist your user's data.
32 | |
33 | | Supported: "token"
34 | |
35 | */
36 |
37 | 'guards' => [
38 | 'api' => [
39 | 'driver' => 'paseto',
40 | 'provider' => 'users'
41 | ]
42 | ],
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | User Providers
47 | |--------------------------------------------------------------------------
48 | |
49 | | All authentication drivers have a user provider. This defines how the
50 | | users are actually retrieved out of your database or other storage
51 | | mechanisms used by this application to persist your user's data.
52 | |
53 | | If you have multiple user tables or models you may configure multiple
54 | | sources which represent each model / table. These sources may then
55 | | be assigned to any extra authentication guards you have defined.
56 | |
57 | | Supported: "database", "eloquent"
58 | |
59 | */
60 |
61 | 'providers' => [
62 | 'users' => [
63 | 'driver' => 'eloquent',
64 | 'model' => \App\User::class,
65 | ]
66 | ],
67 |
68 | /*
69 | |--------------------------------------------------------------------------
70 | | Resetting Passwords
71 | |--------------------------------------------------------------------------
72 | |
73 | | Here you may set the options for resetting passwords including the view
74 | | that is your password reset e-mail. You may also set the name of the
75 | | table that maintains all of the reset tokens for your application.
76 | |
77 | | You may specify multiple password reset configurations if you have more
78 | | than one user table or model in the application and you want to have
79 | | separate password reset settings based on the specific user types.
80 | |
81 | | The expire time is the number of minutes that the reset token should be
82 | | considered valid. This security feature keeps tokens short-lived so
83 | | they have less time to be guessed. You may change this as needed.
84 | |
85 | */
86 |
87 | 'passwords' => [
88 | //
89 | ],
90 |
91 | ];
92 |
--------------------------------------------------------------------------------
/config/constants.php:
--------------------------------------------------------------------------------
1 | env('WEBSITE_NAME','Acme Inc'),
17 | 'website_email' => env('WEBSITE_EMAIL','contact@acme.inc'),
18 | 'frontend_url' => env('FRONTEND_URL', 'http://127.0.0.1:8080'),
19 | ];
20 |
--------------------------------------------------------------------------------
/config/mail.php:
--------------------------------------------------------------------------------
1 | env('MAIL_DRIVER', 'log'),
17 | /*
18 | |--------------------------------------------------------------------------
19 | | SMTP Host Address
20 | |--------------------------------------------------------------------------
21 | |
22 | | Here you may provide the host address of the SMTP server used by your
23 | | applications. A default option is provided that is compatible with
24 | | the Mailgun mail service which will provide reliable deliveries.
25 | |
26 | */
27 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
28 | /*
29 | |--------------------------------------------------------------------------
30 | | SMTP Host Port
31 | |--------------------------------------------------------------------------
32 | |
33 | | This is the SMTP port used by your application to deliver e-mails to
34 | | users of the application. Like the host we have set this value to
35 | | stay compatible with the Mailgun e-mail application by default.
36 | |
37 | */
38 | 'port' => env('MAIL_PORT', 587),
39 | /*
40 | |--------------------------------------------------------------------------
41 | | Global "From" Address
42 | |--------------------------------------------------------------------------
43 | |
44 | | You may wish for all e-mails sent by your application to be sent from
45 | | the same address. Here, you may specify a name and address that is
46 | | used globally for all e-mails that are sent by your application.
47 | |
48 | */
49 | 'from' => [
50 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
51 | 'name' => env('MAIL_FROM_NAME', 'Example'),
52 | ],
53 | /*
54 | |--------------------------------------------------------------------------
55 | | E-Mail Encryption Protocol
56 | |--------------------------------------------------------------------------
57 | |
58 | | Here you may specify the encryption protocol that should be used when
59 | | the application send e-mail messages. A sensible default using the
60 | | transport layer security protocol should provide great security.
61 | |
62 | */
63 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'),
64 | /*
65 | |--------------------------------------------------------------------------
66 | | SMTP Server Username
67 | |--------------------------------------------------------------------------
68 | |
69 | | If your SMTP server requires a username for authentication, you should
70 | | set it here. This will get used to authenticate with your server on
71 | | connection. You may also set the "password" value below this one.
72 | |
73 | */
74 | 'username' => env('MAIL_USERNAME'),
75 | 'password' => env('MAIL_PASSWORD'),
76 | /*
77 | |--------------------------------------------------------------------------
78 | | Sendmail System Path
79 | |--------------------------------------------------------------------------
80 | |
81 | | When using the "sendmail" driver to send e-mails, we will need to know
82 | | the path to where Sendmail lives on this server. A default path has
83 | | been provided here, which will work well on most of your systems.
84 | |
85 | */
86 | 'sendmail' => '/usr/sbin/sendmail -bs',
87 | /*
88 | |--------------------------------------------------------------------------
89 | | Markdown Mail Settings
90 | |--------------------------------------------------------------------------
91 | |
92 | | If you are using Markdown based email rendering, you may configure your
93 | | theme and component paths here, allowing you to customize the design
94 | | of the emails. Or, you may simply stick with the Laravel defaults!
95 | |
96 | */
97 | 'markdown' => [
98 | 'theme' => 'default',
99 | 'paths' => [
100 | resource_path('views/vendor/mail'),
101 | ],
102 | ],
103 | ];
--------------------------------------------------------------------------------
/config/permission.php:
--------------------------------------------------------------------------------
1 | [
6 |
7 | /*
8 | * When using the "HasRoles" trait from this package, we need to know which
9 | * Eloquent model should be used to retrieve your permissions. Of course, it
10 | * is often just the "Permission" model but you may use whatever you like.
11 | *
12 | * The model you want to use as a Permission model needs to implement the
13 | * `Spatie\Permission\Contracts\Permission` contract.
14 | */
15 |
16 | 'permission' => Spatie\Permission\Models\Permission::class,
17 |
18 | /*
19 | * When using the "HasRoles" trait from this package, we need to know which
20 | * Eloquent model should be used to retrieve your roles. Of course, it
21 | * is often just the "Role" model but you may use whatever you like.
22 | *
23 | * The model you want to use as a Role model needs to implement the
24 | * `Spatie\Permission\Contracts\Role` contract.
25 | */
26 |
27 | 'role' => Spatie\Permission\Models\Role::class,
28 |
29 | ],
30 |
31 | 'table_names' => [
32 |
33 | /*
34 | * When using the "HasRoles" trait from this package, we need to know which
35 | * table should be used to retrieve your roles. We have chosen a basic
36 | * default value but you may easily change it to any table you like.
37 | */
38 |
39 | 'roles' => 'roles',
40 |
41 | /*
42 | * When using the "HasRoles" trait from this package, we need to know which
43 | * table should be used to retrieve your permissions. We have chosen a basic
44 | * default value but you may easily change it to any table you like.
45 | */
46 |
47 | 'permissions' => 'permissions',
48 |
49 | /*
50 | * When using the "HasRoles" trait from this package, we need to know which
51 | * table should be used to retrieve your models permissions. We have chosen a
52 | * basic default value but you may easily change it to any table you like.
53 | */
54 |
55 | 'model_has_permissions' => 'model_has_permissions',
56 |
57 | /*
58 | * When using the "HasRoles" trait from this package, we need to know which
59 | * table should be used to retrieve your models roles. We have chosen a
60 | * basic default value but you may easily change it to any table you like.
61 | */
62 |
63 | 'model_has_roles' => 'model_has_roles',
64 |
65 | /*
66 | * When using the "HasRoles" trait from this package, we need to know which
67 | * table should be used to retrieve your roles permissions. We have chosen a
68 | * basic default value but you may easily change it to any table you like.
69 | */
70 |
71 | 'role_has_permissions' => 'role_has_permissions',
72 | ],
73 |
74 | /*
75 | * By default all permissions will be cached for 24 hours unless a permission or
76 | * role is updated. Then the cache will be flushed immediately.
77 | */
78 |
79 | 'cache_expiration_time' => 60 * 24,
80 |
81 | /*
82 | * When set to true, the required permission/role names are added to the exception
83 | * message. This could be considered an information leak in some contexts, so
84 | * the default setting is false here for optimum safety.
85 | */
86 |
87 | 'display_permission_in_exception' => false,
88 | ];
89 |
--------------------------------------------------------------------------------
/database/factories/ModelFactory.php:
--------------------------------------------------------------------------------
1 | increments('id');
20 | $table->string('name');
21 | $table->string('guard_name');
22 | $table->timestamps();
23 | });
24 |
25 | Schema::create($tableNames['roles'], function (Blueprint $table) {
26 | $table->increments('id');
27 | $table->string('name');
28 | $table->string('guard_name');
29 | $table->timestamps();
30 | });
31 |
32 | Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames) {
33 | $table->unsignedInteger('permission_id');
34 | $table->morphs('model');
35 |
36 | $table->foreign('permission_id')
37 | ->references('id')
38 | ->on($tableNames['permissions'])
39 | ->onDelete('cascade');
40 |
41 | $table->primary(['permission_id', 'model_id', 'model_type'], 'model_has_permissions_permission_model_type_primary');
42 | });
43 |
44 | Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames) {
45 | $table->unsignedInteger('role_id');
46 | $table->morphs('model');
47 |
48 | $table->foreign('role_id')
49 | ->references('id')
50 | ->on($tableNames['roles'])
51 | ->onDelete('cascade');
52 |
53 | $table->primary(['role_id', 'model_id', 'model_type']);
54 | });
55 |
56 | Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
57 | $table->unsignedInteger('permission_id');
58 | $table->unsignedInteger('role_id');
59 |
60 | $table->foreign('permission_id')
61 | ->references('id')
62 | ->on($tableNames['permissions'])
63 | ->onDelete('cascade');
64 |
65 | $table->foreign('role_id')
66 | ->references('id')
67 | ->on($tableNames['roles'])
68 | ->onDelete('cascade');
69 |
70 | $table->primary(['permission_id', 'role_id']);
71 |
72 | app('cache')->forget('spatie.permission.cache');
73 | });
74 | }
75 |
76 | /**
77 | * Reverse the migrations.
78 | *
79 | * @return void
80 | */
81 | public function down()
82 | {
83 | $tableNames = config('permission.table_names');
84 |
85 | Schema::drop($tableNames['role_has_permissions']);
86 | Schema::drop($tableNames['model_has_roles']);
87 | Schema::drop($tableNames['model_has_permissions']);
88 | Schema::drop($tableNames['roles']);
89 | Schema::drop($tableNames['permissions']);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/database/migrations/2018_03_03_232931_create_users_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('name')->nullable();
18 | $table->string('email')->unique();
19 | $table->string('password', 60);
20 | $table->boolean('verified')->default(false);
21 | $table->string('verification_token')->nullable();
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | *
29 | * @return void
30 | */
31 | public function down()
32 | {
33 | Schema::dropIfExists('users');
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/database/migrations/2018_03_03_232948_create_password_resets_table.php:
--------------------------------------------------------------------------------
1 | string('email')->index();
17 | $table->string('token')->index();
18 | $table->timestamps();
19 | });
20 | }
21 |
22 | /**
23 | * Reverse the migrations.
24 | *
25 | * @return void
26 | */
27 | public function down()
28 | {
29 | Schema::dropIfExists('password_resets');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/database/seeds/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | call([
16 | RolesSeeder::class,
17 | ]);
18 | User::createFromValues('John Doe', 'demo@demo.com', 'password')->assignRole('administrator');
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/database/seeds/RolesSeeder.php:
--------------------------------------------------------------------------------
1 | forget('spatie.permission.cache');
13 |
14 | $role = Role::create(['name' => 'administrator']);
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/helpers.php:
--------------------------------------------------------------------------------
1 |
2 |