Browse Source

Added settings to config

Will Browning 5 years ago
parent
commit
c6bf93f44c

+ 13 - 13
.env.example

@@ -1,8 +1,8 @@
-APP_NAME=Laravel
+APP_NAME=Example
 APP_ENV=local
 APP_KEY=
 APP_DEBUG=true
-APP_URL=http://localhost
+APP_URL=https://example.com
 
 LOG_CHANNEL=stack
 
@@ -30,8 +30,8 @@ MAIL_USERNAME=null
 MAIL_PASSWORD=null
 MAIL_ENCRYPTION=null
 
-MAIL_FROM_NAME=AnonAddy
-MAIL_FROM_ADDRESS=mailer@anonaddy.me
+MAIL_FROM_NAME=Example
+MAIL_FROM_ADDRESS=mailer@example.com
 
 PUSHER_APP_ID=
 PUSHER_APP_KEY=
@@ -41,16 +41,16 @@ PUSHER_APP_CLUSTER=mt1
 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
 MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
 
-ENVOY_USER_AND_SERVER=username@server
-ENVOY_BASE_DIR=/path/to/app/directory
-ENVOY_RUN_TESTS="ssh -tt -p22 vagrant@192.168.10.10 '/path/to/homestead/app/vendor/bin/phpunit --configuration /path/to/homestead/app/phpunit.xml'"
-
-ANONADDY_RETURN_PATH=bounces@anonaddy.me
+ANONADDY_RETURN_PATH=bounces@example.com
 ANONADDY_ADMIN_USERNAME=johndoe
-ANONADDY_DOMAIN=anonaddy.me
-ANONADDY_ALL_DOMAINS=anonaddy.me,anonaddy.com
+ANONADDY_ENABLE_REGISTRATION=true
+ANONADDY_DOMAIN=example.com
+ANONADDY_HOSTNAME=mail.example.com
+ANONADDY_DNS_RESOLVER=127.0.0.1
+ANONADDY_ALL_DOMAINS=example.com,example2.com
 ANONADDY_SECRET=long-random-string
 ANONADDY_LIMIT=200
-ANONADDY_NEWSLETTER_URL=https://newsletter.yourdomain.com/subscribe
-ANONADDY_NEWSLETTER_LIST=your-list-id
+ANONADDY_BANDWIDTH_LIMIT=104857600
+ANONADDY_NEW_ALIAS_LIMIT=10
+ANONADDY_ADDITIONAL_USERNAME_LIMIT=3
 ANONADDY_SIGNING_KEY_FINGERPRINT=your-signing-key-fingerprint

+ 0 - 175
Envoy.blade.php

@@ -1,175 +0,0 @@
-@setup
-require __DIR__."/vendor/autoload.php";
-$dotenv = Dotenv\Dotenv::create(__DIR__, ".env");
-$dotenv->load();
-
-$userAndServer = getenv("ENVOY_USER_AND_SERVER");
-$repository = "anonaddy/anonaddy";
-$baseDir = getenv("ENVOY_BASE_DIR");
-$releasesDir = "{$baseDir}/releases";
-$persistentDir = "{$baseDir}/persistent";
-$currentDir = "{$baseDir}/current";
-$newReleaseName = date("Ymd-His");
-$newReleaseDir = "{$releasesDir}/{$newReleaseName}";
-
-function logMessage($message) {
-  return "echo '\033[32m" .$message. "\033[0m';\n";
-}
-@endsetup
-
-@servers(["local" => "127.0.0.1", "remote" => $userAndServer])
-
-@story("deploy")
-startDeployment
-runTests
-cloneRepository
-runComposer
-runNpm
-generateAssets
-updateSymlinks
-optimizeInstallation
-migrateDatabase
-blessNewRelease
-cleanOldReleases
-finishDeploy
-@endstory
-
-@story("deploy-code")
-runTests
-deployOnlyCode
-@endstory
-
-@story("deploy-rollback")
-deploymentRollback
-@endstory
-
-@task("startDeployment", ["on" => "local"])
-{{ logMessage("🏃  Starting deployment...") }}
-@endtask
-
-@task("runTests", ["on" => "local"])
-{{ logMessage("💻  Running Unit Tests...") }}
-env -i bash -c "{{ getenv('ENVOY_RUN_TESTS') }}" || exit 1
-@endtask
-
-@task("cloneRepository", ["on" => "remote"])
-{{ logMessage("🌀  Cloning repository...") }}
-[ -d {{ $releasesDir }} ] || mkdir {{ $releasesDir }}
-[ -d {{ $persistentDir }} ] || mkdir {{ $persistentDir }}
-[ -d {{ $persistentDir }}/storage ] || mkdir {{ $persistentDir }}/storage
-cd {{ $releasesDir }}
-
-# Create the release dir
-mkdir {{ $newReleaseDir }}
-
-# Clone the repo
-git clone --depth 1 git@github.com:{{ $repository }} {{ $newReleaseName }}
-
-# Configure sparse checkout
-cd {{ $newReleaseDir }}
-git config core.sparsecheckout true
-echo "*" > .git/info/sparse-checkout
-echo "!storage" >> .git/info/sparse-checkout
-echo "!public/build" >> .git/info/sparse-checkout
-git read-tree -mu HEAD
-
-# Mark release
-cd {{ $newReleaseDir }}
-echo "{{ $newReleaseName }}" > public/release-name.txt
-@endtask
-
-@task("runComposer", ["on" => "remote"])
-{{ logMessage("🚚  Running Composer...") }}
-cd {{ $newReleaseDir }}
-composer install --prefer-dist --no-scripts --no-dev -q -o
-@endtask
-
-@task("runNpm", ["on" => "remote"])
-{{ logMessage("📦  Running Npm...") }}
-cd {{ $newReleaseDir }}
-npm install --no-progress &> /dev/null
-@endtask
-
-@task("generateAssets", ["on" => "remote"])
-{{ logMessage("🌅  Generating assets...") }}
-cd {{ $newReleaseDir }}
-npm run production --no-progress &> /dev/null
-@endtask
-
-@task("updateSymlinks", ["on" => "remote"])
-{{ logMessage("🔗  Updating symlinks to persistent data...") }}
-# Remove the storage directory and replace with persistent data
-rm -rf {{ $newReleaseDir }}/storage
-cd {{ $newReleaseDir }}
-ln -nfs {{ $baseDir }}/persistent/storage storage
-
-# Import the environment config
-cd {{ $newReleaseDir }}
-ln -nfs {{ $baseDir }}/.env .env
-@endtask
-
-@task("optimizeInstallation", ["on" => "remote"])
-{{ logMessage("✨  Optimizing installation...") }}
-cd {{ $newReleaseDir }}
-php artisan clear-compiled
-@endtask
-
-@task("migrateDatabase", ["on" => "remote"])
-{{ logMessage("🙈  Migrating database...") }}
-cd {{ $newReleaseDir }}
-php artisan migrate --force
-@endtask
-
-@task("blessNewRelease", ["on" => "remote"])
-{{ logMessage("🙏  Blessing new release...") }}
-ln -nfs {{ $newReleaseDir }} {{ $currentDir }}
-cd {{ $newReleaseDir }}
-
-php artisan storage:link
-
-php artisan config:clear
-php artisan view:clear
-php artisan cache:clear
-
-php artisan config:cache
-php artisan view:cache
-php artisan route:cache
-
-php artisan queue:restart
-@endtask
-
-@task("cleanOldReleases", ["on" => "remote"])
-{{ logMessage("🚾  Cleaning up old releases...") }}
-# Delete all but the 5 most recent.
-cd {{ $releasesDir }}
-ls -dt {{ $releasesDir }}/* | tail -n +6 | xargs -d "\n" chown -R deployer .
-ls -dt {{ $releasesDir }}/* | tail -n +6 | xargs -d "\n" rm -rf
-@endtask
-
-@task("finishDeploy", ["on" => "local"])
-{{ logMessage("🚀  Application deployed!") }}
-@endtask
-
-@task("deployOnlyCode",["on" => "remote"])
-{{ logMessage("💻  Deploying code changes...") }}
-cd {{ $currentDir }}
-git pull origin master
-
-php artisan storage:link
-
-php artisan config:clear
-php artisan view:clear
-php artisan cache:clear
-
-php artisan config:cache
-php artisan view:cache
-php artisan route:cache
-
-php artisan queue:restart
-@endtask
-
-@task("deploymentRollback", ["on" => "remote"])
-cd {{ $releasesDir }}
-ln -nfs {{ $releasesDir }}/$(find . -maxdepth 1 -name "20*" | sort  | tail -n 2 | head -n1) {{ $baseDir }}/current
-echo "Rolled back to $(find . -maxdepth 1 -name "20*" | sort  | tail -n 2 | head -n1)"
-@endtask

+ 1 - 1
README.md

@@ -1,6 +1,6 @@
 # Anonymous Email Forwarding
 
-This is the source code for [app.anonaddy.com](https://app.anonaddy.com).
+This is the source code for self-hosting AnonAddy.
 
 ## FAQ
 

+ 9 - 0
app/AdditionalUsername.php

@@ -33,6 +33,15 @@ class AdditionalUsername extends Model
         'active' => 'boolean'
     ];
 
+    public static function boot()
+    {
+        parent::boot();
+
+        AdditionalUsername::deleting(function ($username) {
+            DeletedUsername::create(['username' => $username->username]);
+        });
+    }
+
     /**
      * Set the username.
      */

+ 9 - 31
app/Alias.php

@@ -88,52 +88,30 @@ class Alias extends Model
     }
 
     /**
-     * Get the verified emails of recipients not using PGP for the email alias.
+     * Get the verified recipients for the email alias or the default recipient if none are set.
      */
-    public function nonPgpRecipientEmails()
+    public function verifiedRecipientsOrDefault()
     {
-        if ($this->verifiedRecipients()->count() === 0 && !$this->user->defaultRecipient->should_encrypt) {
-            return [$this->user->email];
-        }
-
-        return $this
-                ->verifiedRecipients()
-                ->where('should_encrypt', false)
-                ->get()
-                ->map(function ($recipient) {
-                    return $recipient->email;
-                })->toArray();
-    }
-
-    /**
-     * Check if the email alias has any recipients not using PGP.
-     */
-    public function hasNonPgpRecipients()
-    {
-        return count($this->nonPgpRecipientEmails()) > 0;
-    }
-
-    /**
-     * Get the verified recipients using PGP for the email alias.
-     */
-    public function recipientsUsingPgp()
-    {
-        if ($this->verifiedRecipients()->count() === 0 && $this->user->defaultRecipient->should_encrypt) {
+        if ($this->verifiedRecipients()->count() === 0) {
             return $this->user->defaultRecipient();
         }
 
         return $this
                 ->verifiedRecipients()
-                ->where('should_encrypt', true)
-                ->whereNotNull('fingerprint')
                 ->get();
     }
 
+    /**
+     * Deactivate the alias.
+     */
     public function deactivate()
     {
         $this->update(['active' => false]);
     }
 
+    /**
+     * Activate the alias.
+     */
     public function activate()
     {
         $this->update(['active' => true]);

+ 11 - 18
app/Console/Commands/ReceiveEmail.php

@@ -68,7 +68,7 @@ class ReceiveEmail extends Command
 
             $recipients = $this->getRecipients();
 
-            // Divide the size of the email by the number of recipients (excluding any unsubscribe recipients) to prevent it being added multiple times
+            // Divide the size of the email by the number of recipients (excluding any unsubscribe recipients) to prevent it being added multiple times.
             $recipientCount = $recipients->where('domain', '!=', 'unsubscribe.'.config('anonaddy.domain'))->count();
 
             $this->size = $this->option('size') / ($recipientCount ? $recipientCount : 1);
@@ -82,7 +82,7 @@ class ReceiveEmail extends Command
                     })
                     ->first();
 
-                $subdomain = substr($recipient['domain'], 0, strrpos($recipient['domain'], '.'.$parentDomain)); // e.g. johndoe
+                $subdomain = substr($recipient['domain'], 0, strrpos($recipient['domain'], '.'.$parentDomain));
 
                 if ($subdomain === 'unsubscribe') {
                     $this->handleUnsubscribe($recipient);
@@ -91,19 +91,18 @@ class ReceiveEmail extends Command
 
                 $user = User::where('username', $subdomain)->first();
 
-                // If no user is found for the subdomain check if it is a custom or root domain instead
                 if (is_null($user)) {
-                    // check if this is a custom domain
+                    // Check if this is a custom domain.
                     if ($customDomain = Domain::where('domain', $recipient['domain'])->first()) {
                         $user = $customDomain->user;
                     }
 
-                    // check if this is an additional username
+                    // Check if this is an additional username.
                     if ($additionalUsername = AdditionalUsername::where('username', $subdomain)->first()) {
                         $user = $additionalUsername->user;
                     }
 
-                    // check if this is a uuid generated alias
+                    // Check if this is a uuid generated alias.
                     if ($alias = Alias::find($recipient['local_part'])) {
                         $user = $alias->user;
                     } elseif ($recipient['domain'] === $parentDomain && !empty(config('anonaddy.admin_username'))) {
@@ -111,14 +110,14 @@ class ReceiveEmail extends Command
                     }
                 }
 
-                // If there is still no user or the user has no verified default recipient then continue
+                // If there is still no user or the user has no verified default recipient then continue.
                 if (is_null($user) || !$user->hasVerifiedDefaultRecipient()) {
                     continue;
                 }
 
                 $this->checkRateLimit($user);
 
-                // check whether this email is a reply or a new email to be forwarded
+                // Check whether this email is a reply or a new email to be forwarded.
                 if ($recipient['extension'] === sha1(config('anonaddy.secret').$displayTo)) {
                     $this->handleReply($user, $recipient, $displayTo);
                 } else {
@@ -177,7 +176,7 @@ class ReceiveEmail extends Command
         ]);
 
         if (!isset($alias->id)) {
-            // this is a new alias
+            // This is a new alias.
             if ($user->hasExceededNewAliasLimit()) {
                 $this->error('4.2.1 New aliases per hour limit exceeded for user ' . $user->username . '.');
 
@@ -211,18 +210,12 @@ class ReceiveEmail extends Command
 
         $emailData = new EmailData($this->parser);
 
-        $alias->recipientsUsingPgp()->each(function ($recipient) use ($alias, $emailData) {
-            $message = (new ForwardEmail($alias, $emailData, $recipient->fingerprint))->onQueue('default');
+        $alias->verifiedRecipientsOrDefault()->each(function ($recipient) use ($alias, $emailData) {
+            $message = (new ForwardEmail($alias, $emailData, $recipient->should_encrypt ? $recipient->fingerprint : null))->onQueue('default');
 
             Mail::to($recipient->email)->queue($message);
         });
 
-        if ($alias->hasNonPgpRecipients()) {
-            $message = (new ForwardEmail($alias, $emailData))->onQueue('default');
-
-            Mail::to($alias->nonPgpRecipientEmails())->queue($message);
-        }
-
         if (!Mail::failures()) {
             $alias->increment('emails_forwarded');
 
@@ -283,7 +276,7 @@ class ReceiveEmail extends Command
 
     protected function exitIfFromSelf()
     {
-        // To prevent recipient alias infinite nested looping
+        // To prevent recipient alias infinite nested looping.
         if (in_array($this->option('sender'), [config('mail.from.address'), config('anonaddy.return_path')])) {
             exit(0);
         }

+ 2 - 2
app/Domain.php

@@ -105,9 +105,9 @@ class Domain extends Model
      */
     public function checkVerification()
     {
-        $dns = new Dns($this->domain, '1.1.1.1');
+        $dns = new Dns($this->domain, config('anonaddy.dns_resolver'));
 
-        if (Str::contains($dns->getRecords('MX'), 'MX 10 mail.anonaddy.me.')) {
+        if (Str::contains($dns->getRecords('MX'), 'MX 10 ' . config('anonaddy.hostname') . '.')) {
             $this->markDomainAsVerified();
         }
     }

+ 2 - 2
app/Http/Controllers/AliasController.php

@@ -32,9 +32,9 @@ class AliasController extends Controller
 
         $alias = user()->aliases()->create([
             'id' => $uuid,
-            'email' => $uuid . '@anonaddy.me',
+            'email' => $uuid.'@'.config('anonaddy.domain'),
             'local_part' => $uuid,
-            'domain' => 'anonaddy.me'
+            'domain' => config('anonaddy.domain')
         ]);
 
         return new AliasResource($alias->fresh());

+ 0 - 7
app/Http/Controllers/Auth/RegisterController.php

@@ -3,7 +3,6 @@
 namespace App\Http\Controllers\Auth;
 
 use App\Http\Controllers\Controller;
-use App\Jobs\SubscribeToNewsletter;
 use App\Recipient;
 use App\Rules\NotBlacklisted;
 use App\Rules\NotDeletedUsername;
@@ -73,8 +72,6 @@ class RegisterController extends Controller
                 new RegisterUniqueRecipient
             ],
             'password' => ['required', 'min:8'],
-            'newsletter' => ['nullable'],
-            'terms' => ['required', 'accepted'],
         ], [
             'captcha.captcha' => 'The text entered was incorrect, please try again.',
         ])
@@ -98,10 +95,6 @@ class RegisterController extends Controller
             'user_id' => $userId
         ]);
 
-        if (isset($data['newsletter'])) {
-            SubscribeToNewsletter::dispatch($data['email']);
-        }
-
         $twoFactor = app('pragmarx.google2fa');
 
         return User::create([

+ 1 - 1
app/Http/Requests/UpdateBannerLocationRequest.php

@@ -13,7 +13,7 @@ class UpdateBannerLocationRequest extends FormRequest
      */
     public function authorize()
     {
-        return true; // Pro plan
+        return true;
     }
 
     /**

+ 1 - 1
app/Http/Requests/UpdateFromNameRequest.php

@@ -13,7 +13,7 @@ class UpdateFromNameRequest extends FormRequest
      */
     public function authorize()
     {
-        return true; // Pro plan
+        return true;
     }
 
     /**

+ 2 - 2
app/Jobs/DeleteAccount.php

@@ -38,9 +38,9 @@ class DeleteAccount implements ShouldQueue
         $this->user->aliasRecipients()->delete();
         $this->user->aliases()->whereNull('domain_id')->forceDelete();
         $this->user->aliases()->delete();
-        $this->user->recipients()->get()->each->delete(); // in order to fire deleting model event
+        $this->user->recipients()->get()->each->delete(); // In order to fire deleting model event.
         $this->user->domains()->delete();
-        $this->user->additionalUsernames()->delete();
+        $this->user->additionalUsernames()->get()->each->delete(); // In order to fire deleting model event.
         $this->user->delete();
     }
 }

+ 0 - 48
app/Jobs/SubscribeToNewsletter.php

@@ -1,48 +0,0 @@
-<?php
-
-namespace App\Jobs;
-
-use Illuminate\Bus\Queueable;
-use Illuminate\Contracts\Queue\ShouldQueue;
-use Illuminate\Foundation\Bus\Dispatchable;
-use Illuminate\Queue\InteractsWithQueue;
-use Illuminate\Queue\SerializesModels;
-
-class SubscribeToNewsletter implements ShouldQueue
-{
-    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
-
-    protected $email;
-
-    /**
-     * Create a new job instance.
-     *
-     * @return void
-     */
-    public function __construct($email)
-    {
-        $this->email = $email;
-    }
-
-    /**
-     * Execute the job.
-     *
-     * @return void
-     */
-    public function handle()
-    {
-        $data = [
-            'email' => $this->email,
-            'list' => config('anonaddy.newsletter_list'),
-            'gdpr' => "true"
-        ];
-
-        $ch = curl_init(config('anonaddy.newsletter_url'));
-        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-        curl_setopt($ch, CURLOPT_POST, true);
-        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data, '', '&'));
-
-        curl_exec($ch);
-        curl_close($ch);
-    }
-}

+ 3 - 4
app/User.php

@@ -201,8 +201,7 @@ class User extends Authenticatable implements MustVerifyEmail
 
     public function getBandwidthLimit()
     {
-        // TODO check user's limit and return
-        return 104857600;
+        return config('anonaddy.bandwidth_limit');
     }
 
     public function getBandwidthLimitMb()
@@ -221,12 +220,12 @@ class User extends Authenticatable implements MustVerifyEmail
         return $this
                 ->aliases()
                 ->where('created_at', '>=', now()->subHour())
-                ->count() >= 10; // TODO update for different plans
+                ->count() >= config('anonaddy.new_alias_hourly_limit');
     }
 
     public function hasReachedAdditionalUsernameLimit()
     {
-        return $this->username_count >= 3;
+        return $this->username_count >= config('anonaddy.additional_username_limit');
     }
 
     public function isVerifiedRecipient($email)

+ 38 - 38
composer.lock

@@ -46,9 +46,9 @@
             "authors": [
                 {
                     "name": "Ben Scholzen 'DASPRiD'",
+                    "role": "Developer",
                     "email": "mail@dasprids.de",
-                    "homepage": "http://www.dasprids.de",
-                    "role": "Developer"
+                    "homepage": "http://www.dasprids.de"
                 }
             ],
             "description": "BaconQrCode is a QR code generator for PHP.",
@@ -1583,15 +1583,15 @@
             "authors": [
                 {
                     "name": "Paragon Initiative Enterprises",
+                    "role": "Maintainer",
                     "email": "security@paragonie.com",
-                    "homepage": "https://paragonie.com",
-                    "role": "Maintainer"
+                    "homepage": "https://paragonie.com"
                 },
                 {
                     "name": "Steve 'Sc00bz' Thomas",
+                    "role": "Original Developer",
                     "email": "steve@tobtu.com",
-                    "homepage": "https://www.tobtu.com",
-                    "role": "Original Developer"
+                    "homepage": "https://www.tobtu.com"
                 }
             ],
             "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
@@ -1831,8 +1831,8 @@
             "authors": [
                 {
                     "name": "Antonio Carlos Ribeiro",
-                    "email": "acr@antoniocarlosribeiro.com",
-                    "role": "Creator & Designer"
+                    "role": "Creator & Designer",
+                    "email": "acr@antoniocarlosribeiro.com"
                 }
             ],
             "description": "A One Time Password Authentication package, compatible with Google Authenticator.",
@@ -1902,8 +1902,8 @@
             "authors": [
                 {
                     "name": "Antonio Carlos Ribeiro",
-                    "email": "acr@antoniocarlosribeiro.com",
-                    "role": "Creator & Designer"
+                    "role": "Creator & Designer",
+                    "email": "acr@antoniocarlosribeiro.com"
                 }
             ],
             "description": "A One Time Password Authentication package, compatible with Google Authenticator.",
@@ -1958,8 +1958,8 @@
             "authors": [
                 {
                     "name": "Antonio Carlos Ribeiro",
-                    "email": "acr@antoniocarlosribeiro.com",
-                    "role": "Creator & Designer"
+                    "role": "Creator & Designer",
+                    "email": "acr@antoniocarlosribeiro.com"
                 }
             ],
             "description": "QR Code package for Google2FA",
@@ -3985,8 +3985,8 @@
             "authors": [
                 {
                     "name": "Tijs Verkoyen",
-                    "email": "css_to_inline_styles@verkoyen.eu",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "css_to_inline_styles@verkoyen.eu"
                 }
             ],
             "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
@@ -4800,18 +4800,18 @@
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "arne@blankerts.de"
                 },
                 {
                     "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpeople.de"
                 },
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
@@ -4847,18 +4847,18 @@
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "arne@blankerts.de"
                 },
                 {
                     "name": "Sebastian Heuer",
-                    "email": "sebastian@phpeople.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpeople.de"
                 },
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Library for handling version information and constraints",
@@ -5231,8 +5231,8 @@
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "FilterIterator implementation that filters files based on a list of suffixes.",
@@ -5273,8 +5273,8 @@
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Simple template engine.",
@@ -5322,8 +5322,8 @@
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Utility class for timing",
@@ -6029,8 +6029,8 @@
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Collection of value objects that represent the types of the PHP type system",
@@ -6072,8 +6072,8 @@
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sebastian@phpunit.de",
-                    "role": "lead"
+                    "role": "lead",
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Library that helps with managing the version number of Git-hosted PHP projects",
@@ -6326,8 +6326,8 @@
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "arne@blankerts.de"
                 }
             ],
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",

+ 62 - 12
config/anonaddy.php

@@ -19,31 +19,67 @@ return [
     |--------------------------------------------------------------------------
     |
     | If set this value will be used and allow you to receive forwarded emails
-    | at the root domain, e.g. @anonaddy.me aswell as @username.anonaddy.me
+    | at the root domain, e.g. @example.com aswell as @username.example.com
     |
     */
 
     'admin_username' => env('ANONADDY_ADMIN_USERNAME'),
 
+    /*
+    |--------------------------------------------------------------------------
+    | Enable Registration
+    |--------------------------------------------------------------------------
+    |
+    | If set to false this will prevent new users from registering on the site
+    | useful if you are self-hosting and do not want anyone else to be able to register
+    |
+    */
+
+    'enable_registration' => env('ANONADDY_ENABLE_REGISTRATION', true),
+
     /*
     |--------------------------------------------------------------------------
     | Domain
     |--------------------------------------------------------------------------
     |
     | If set and you are self hosting AnonAddy then a check will be done so that you can
-    | receive email at the root domain, e.g. @yourdomain.com aswell as @username.yourdomain.com
+    | receive email at the root domain, e.g. @example.com aswell as @username.example.com
     |
     */
 
     'domain' => env('ANONADDY_DOMAIN'),
 
+    /*
+    |--------------------------------------------------------------------------
+    | Hostname
+    |--------------------------------------------------------------------------
+    |
+    | This value is your FQDN hostname for your server e.g. mail.example.com
+    | it is used to validate records on custom domains that are added by users
+    |
+    */
+
+    'hostname' => env('ANONADDY_HOSTNAME'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | DNS Resolver
+    |--------------------------------------------------------------------------
+    |
+    | This value is used when validating records on custom domains that are added
+    | by users, if you don't have a local caching name server you can use 1.1.1.1 etc.
+    |
+    */
+
+    'dns_resolver' => env('ANONADDY_DNS_RESOLVER', '127.0.0.1'),
+
     /*
     |--------------------------------------------------------------------------
     | All Domains
     |--------------------------------------------------------------------------
     |
-    | If you would like to have other domains to use e.g. @username.yourdomain2.com
-    | enter a comma separated list in your .env file like so, anonaddy.me,anonaddy.com
+    | If you would like to have other domains to use e.g. @username.example2.com
+    | enter a comma separated list in your .env file like so, example.com,example2.com
     |
     */
 
@@ -63,7 +99,7 @@ return [
 
     /*
     |--------------------------------------------------------------------------
-    | Limit
+    | Hourly Email Limit
     |--------------------------------------------------------------------------
     |
     | This value is an integer that determines the number of emails a user can forward
@@ -71,29 +107,43 @@ return [
     |
     */
 
-    'limit' => env('ANONADDY_LIMIT'),
+    'limit' => env('ANONADDY_LIMIT', 200),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Monthly Bandwidth Limit
+    |--------------------------------------------------------------------------
+    |
+    | This value is an integer that determines the monthly bandwidth
+    | limit for users in bytes the default value is 104857600 which is 100MB
+    |
+    */
+
+    'bandwidth_limit' => env('ANONADDY_BANDWIDTH_LIMIT', 104857600),
 
     /*
     |--------------------------------------------------------------------------
-    | Newsletter URL
+    | New Alias Hourly Limit
     |--------------------------------------------------------------------------
     |
-    | This value is the url to subscribe new registrations to the newsletter if they opt in
+    | This value is an integer that determines the number of new aliases
+    | a user can create each hour, the default value is 10 aliases per hour
     |
     */
 
-    'newsletter_url' => env('ANONADDY_NEWSLETTER_URL'),
+    'new_alias_hourly_limit' => env('ANONADDY_NEW_ALIAS_LIMIT', 10),
 
     /*
     |--------------------------------------------------------------------------
-    | Newsletter List
+    | Additional Username Limit
     |--------------------------------------------------------------------------
     |
-    | This value is the ID of the list to subscribe to
+    | This value is an integer that determines the number of additional
+    | usernames a user can add to their account, the default value is 3
     |
     */
 
-    'newsletter_list' => env('ANONADDY_NEWSLETTER_LIST'),
+    'additional_username_limit' => env('ANONADDY_ADDITIONAL_USERNAME_LIMIT', 3),
 
     /*
     |--------------------------------------------------------------------------

+ 10 - 6
resources/js/pages/Aliases.vue

@@ -467,7 +467,7 @@
         </p>
         <p class="mb-4">
           Let's say you're signing up to <b>example.com</b> you could enter
-          <b>example@{{ domain }}</b> (or .me) as your email address.
+          <b>example@{{ subdomain }}</b> as your email address.
         </p>
         <p class="mb-4">
           The alias will show up here automatically as soon as it has forwarded its first email.
@@ -477,7 +477,7 @@
           together!
         </p>
         <p class="mb-4">
-          Try it out now by sending an email to <b>first@{{ domain }}</b> and then refresh this
+          Try it out now by sending an email to <b>first@{{ subdomain }}</b> and then refresh this
           page.
         </p>
         <h3 class="mb-4 text-xl text-indigo-800 font-semibold">
@@ -488,7 +488,7 @@
           like this:
         </p>
         <p class="mb-4">
-          <b>86064c92-da41-443e-a2bf-5a7b0247842f@anonaddy.me</b>
+          <b>86064c92-da41-443e-a2bf-5a7b0247842f@{{ domain }}</b>
         </p>
       </div>
     </div>
@@ -502,11 +502,11 @@
         </h2>
         <p class="mt-4 text-grey-700">
           This will generate a new unique alias in the form of<br /><br />
-          86064c92-da41-443e-a2bf-5a7b0247842f@anonaddy.me<br /><br />
+          86064c92-da41-443e-a2bf-5a7b0247842f@{{ domain }}<br /><br />
           Useful if you do not wish to include your username in the email as a potential link
           between aliases.<br /><br />
-          Other aliases e.g. alias@{{ domain }} or .me are created automatically when they receive
-          their first email.
+          Other aliases e.g. alias@{{ subdomain }} are created automatically when they receive their
+          first email.
         </p>
         <div class="mt-6">
           <button
@@ -643,6 +643,10 @@ export default {
       type: String,
       required: true,
     },
+    subdomain: {
+      type: String,
+      required: true,
+    },
     bandwidthMb: {
       type: Number,
       required: true,

+ 8 - 2
resources/js/pages/Domains.vue

@@ -286,7 +286,8 @@
         </p>
         <p class="mb-4">
           Host: <b>@</b><br />
-          Value: <b>mail.anonaddy.me</b><br />
+          Value: <b>{{ hostname }}</b
+          ><br />
           Priority: <b>10</b><br />
           TTL: <b>3600</b>
         </p>
@@ -306,7 +307,8 @@
         <p class="mt-4 text-grey-700">
           Make sure you add the following MX record to your domain.<br /><br />
           Host: <b>@</b><br />
-          Value: <b>mail.anonaddy.me</b><br />
+          Value: <b>{{ hostname }}</b
+          ><br />
           Priority: <b>10</b><br />
           TTL: <b>3600</b><br /><br />
           Just include the domain/subdomain e.g. example.com without any http protocol.
@@ -385,6 +387,10 @@ export default {
       type: Array,
       required: true,
     },
+    hostname: {
+      type: String,
+      required: true,
+    },
   },
   components: {
     Modal,

+ 8 - 3
resources/js/pages/Usernames.vue

@@ -224,8 +224,8 @@
           and personal emails.
         </p>
         <p>
-          You can add a maximum of 3 additional usernames. Deleted usernames still count towards
-          your limit so please choose carefully.
+          You can add a maximum of {{ usernameCount }} additional usernames. Deleted usernames still
+          count towards your limit so please choose carefully.
         </p>
       </div>
     </div>
@@ -238,7 +238,8 @@
           Add new username
         </h2>
         <p class="mt-4 text-grey-700">
-          Please choose additional usernames carefully as you can only add a maximum of three.
+          Please choose additional usernames carefully as you can only add a maximum of
+          {{ usernameCount }}.
         </p>
         <div class="mt-6">
           <p v-show="errors.newUsername" class="mb-3 text-red-500">
@@ -315,6 +316,10 @@ export default {
       type: Array,
       required: true,
     },
+    usernameCount: {
+      type: Number,
+      required: true,
+    },
   },
   components: {
     Modal,

+ 1 - 1
resources/views/aliases/index.blade.php

@@ -4,6 +4,6 @@
     <div class="container py-8">
         @include('shared.status')
 
-        <aliases :default-recipient="{{json_encode($defaultRecipient)}}" :initial-aliases="{{json_encode($aliases)}}" :recipient-options="{{json_encode($recipients)}}" :total-forwarded="{{$totalForwarded}}" :total-blocked="{{$totalBlocked}}" :total-replies="{{$totalReplies}}" domain="{{$domain}}" :bandwidth-mb="{{$bandwidthMb}}" :month="{{ json_encode(now()->format('M')) }}" />
+        <aliases :default-recipient="{{json_encode($defaultRecipient)}}" :initial-aliases="{{json_encode($aliases)}}" :recipient-options="{{json_encode($recipients)}}" :total-forwarded="{{$totalForwarded}}" :total-blocked="{{$totalBlocked}}" :total-replies="{{$totalReplies}}" domain="{{config('anonaddy.domain')}}" subdomain="{{$domain}}" :bandwidth-mb="{{$bandwidthMb}}" :month="{{ json_encode(now()->format('M')) }}" />
     </div>
 @endsection

+ 0 - 28
resources/views/auth/register.blade.php

@@ -92,34 +92,6 @@
                             @endif
                         </div>
 
-                        <div class="flex flex-wrap mb-3 items-center">
-                            <input type="checkbox" name="newsletter" class="mr-2" id="newsletter">
-
-                            <label class="text-sm text-grey-700" for="newsletter">
-                                Sign up to our newsletter
-                            </label>
-
-                            @if ($errors->has('newsletter'))
-                                <p class="text-red-500 text-xs italic mt-4">
-                                    {{ $errors->first('newsletter') }}
-                                </p>
-                            @endif
-                        </div>
-
-                        <div class="flex flex-wrap items-center">
-                            <input type="checkbox" name="terms" class="mr-2" id="terms">
-
-                            <label class="text-sm text-grey-700" for="terms">
-                                I accept the <a href="https://anonaddy.com/terms" class="text-indigo-800" target="_blank" rel="nofollow noreferrer noopener">terms</a> and <a href="https://anonaddy.com/privacy" class="text-indigo-800" target="_blank" rel="nofollow noreferrer noopener">privacy policy</a>
-                            </label>
-
-                            @if ($errors->has('terms'))
-                                <p class="text-red-500 text-xs italic mt-4">
-                                    {{ $errors->first('terms') }}
-                                </p>
-                            @endif
-                        </div>
-
                     </div>
 
                     <div class="px-6 md:px-10 py-4 bg-grey-50 border-t border-grey-100 flex flex-wrap items-center">

+ 1 - 1
resources/views/domains/index.blade.php

@@ -4,6 +4,6 @@
     <div class="container py-8">
         @include('shared.status')
 
-        <domains :initial-domains="{{json_encode($domains)}}" />
+        <domains :initial-domains="{{json_encode($domains)}}" hostname="{{config('anonaddy.hostname')}}" />
     </div>
 @endsection

+ 0 - 4
resources/views/settings/show.blade.php

@@ -4,10 +4,6 @@
     <div class="container py-8">
         @include('shared.status')
 
-        <div class="text-sm border border-t-8 rounded text-yellow-800 border-yellow-600 bg-yellow-100 px-3 py-4 mb-4" role="alert">
-            We're currently in <b>beta</b>, you'll be able to use all features to test everything out and you'll also have a <b>100MB bandwidth limit</b> each calendar month!
-        </div>
-
         <div class="mb-4">
             <h2 class="text-3xl font-bold">
                 Usage

+ 1 - 1
resources/views/usernames/index.blade.php

@@ -4,6 +4,6 @@
     <div class="container py-8">
         @include('shared.status')
 
-        <usernames :initial-usernames="{{json_encode($usernames)}}" />
+        <usernames :initial-usernames="{{json_encode($usernames)}}" :username-count="{{config('anonaddy.additional_username_limit')}}" />
     </div>
 @endsection

+ 1 - 1
routes/web.php

@@ -11,7 +11,7 @@
 |
 */
 
-Auth::routes(['verify' => true]);
+Auth::routes(['verify' => true, 'register' => config('anonaddy.enable_registration')]);
 
 Route::post('/login/2fa', 'TwoFactorAuthController@authenticateTwoFactor')->name('login.2fa')->middleware(['2fa', 'throttle', 'auth']);
 

+ 11 - 5
tests/Feature/ReceiveEmailTest.php

@@ -362,8 +362,11 @@ class ReceiveEmailTest extends TestCase
         ]);
 
         Mail::assertQueued(ForwardEmail::class, function ($mail) {
-            return $mail->hasTo('one@example.com') &&
-                   $mail->hasTo('two@example.com');
+            return $mail->hasTo('one@example.com');
+        });
+
+        Mail::assertQueued(ForwardEmail::class, function ($mail) {
+            return $mail->hasTo('two@example.com');
         });
     }
 
@@ -409,9 +412,12 @@ class ReceiveEmailTest extends TestCase
             'bandwidth' => '444'
         ]);
 
-        Mail::assertQueued(ForwardEmail::class, function ($mail) use ($recipient, $recipient2) {
-            return $mail->hasTo($recipient->email) &&
-                   $mail->hasTo($recipient2->email);
+        Mail::assertQueued(ForwardEmail::class, function ($mail) use ($recipient) {
+            return $mail->hasTo($recipient->email);
+        });
+
+        Mail::assertQueued(ForwardEmail::class, function ($mail) use ($recipient2) {
+            return $mail->hasTo($recipient2->email);
         });
     }
 

+ 11 - 0
tests/Feature/SettingsTest.php

@@ -2,6 +2,7 @@
 
 namespace Tests\Feature;
 
+use App\AdditionalUsername;
 use App\Alias;
 use App\AliasRecipient;
 use App\DeletedUsername;
@@ -178,6 +179,10 @@ class SettingsTest extends TestCase
             'domain_id' => $domain->id
         ]);
 
+        $additionalUsername = factory(AdditionalUsername::class)->create([
+            'user_id' => $this->user->id
+        ]);
+
         $response = $this->post('/settings/account', [
             'current_password_delete' => 'mypassword'
         ]);
@@ -216,7 +221,13 @@ class SettingsTest extends TestCase
             'user_id' => $this->user->id,
         ]);
 
+        $this->assertDatabaseMissing('additional_usernames', [
+            'id' => $additionalUsername->id,
+            'user_id' => $this->user->id
+        ]);
+
         $this->assertEquals(DeletedUsername::first()->username, $this->user->username);
+        $this->assertEquals(DeletedUsername::skip(1)->first()->username, $additionalUsername->username);
     }
 
     /** @test */

+ 21 - 55
tests/Unit/AliasTest.php

@@ -54,50 +54,6 @@ class AliasTest extends TestCase
         $this->assertEquals($verifiedRecipient->id, $alias->verifiedRecipients[0]->id);
     }
 
-    /** @test */
-    public function alias_can_get_verified_recipient_emails()
-    {
-        $alias = factory(Alias::class)->create([
-            'user_id' => $this->user->id
-        ]);
-
-        $recipientOne = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'email' => 'one@example.com'
-        ]);
-
-        $recipientTwo = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'email' => 'two@example.com'
-        ]);
-
-        $recipientThree = factory(Recipient::class)->create([
-            'user_id' => $this->user->id,
-            'email' => 'three@example.com'
-        ]);
-
-        AliasRecipient::create([
-            'alias' => $alias,
-            'recipient' => $recipientOne
-        ]);
-
-        AliasRecipient::create([
-            'alias' => $alias,
-            'recipient' => $recipientTwo
-        ]);
-
-        AliasRecipient::create([
-            'alias' => $alias,
-            'recipient' => $recipientThree
-        ]);
-
-        $recipientEmails = $alias->nonPgpRecipientEmails();
-
-        $this->assertCount(3, $recipientEmails);
-        $this->assertIsArray($recipientEmails);
-        $this->assertEquals([$recipientOne->email, $recipientTwo->email, $recipientThree->email], $recipientEmails);
-    }
-
     /** @test */
     public function alias_can_set_default_recipient_email()
     {
@@ -119,7 +75,7 @@ class AliasTest extends TestCase
     /** @test */
     public function alias_can_get_default_recipient_email()
     {
-        factory(Alias::class)->create([
+        $alias = factory(Alias::class)->create([
             'user_id' => $this->user->id
         ]);
 
@@ -129,12 +85,17 @@ class AliasTest extends TestCase
         ]);
 
         $this->user->defaultRecipient = $recipient;
+        $this->user->save();
 
         $this->assertEquals($this->user->email, $recipient->email);
+        $this->assertCount(1, $alias->verifiedRecipientsOrDefault()->get());
+        $alias->verifiedRecipientsOrDefault()->each(function ($recipient) {
+            $this->assertEquals($this->user->email, $recipient->email);
+        });
     }
 
     /** @test */
-    public function alias_can_get_recipients_using_pgp_or_not()
+    public function alias_can_get_verified_recipients_or_default()
     {
         $alias = factory(Alias::class)->create([
             'user_id' => $this->user->id
@@ -168,6 +129,14 @@ class AliasTest extends TestCase
             'fingerprint' => null
         ]);
 
+        $recipientFive = factory(Recipient::class)->create([
+            'user_id' => $this->user->id,
+            'email' => 'five@example.com',
+            'should_encrypt' => false,
+            'fingerprint' => null,
+            'email_verified_at' => null
+        ]);
+
         AliasRecipient::create([
             'alias' => $alias,
             'recipient' => $recipientOne
@@ -188,15 +157,12 @@ class AliasTest extends TestCase
             'recipient' => $recipientFour
         ]);
 
-        $pgpRecipients = $alias->recipientsUsingPgp();
-
-        $nonPgpRecipients = $alias->nonPgpRecipientEmails();
-
-        $this->assertCount(1, $nonPgpRecipients);
-        $this->assertIsArray($nonPgpRecipients);
-        $this->assertEquals([$recipientThree->email], $nonPgpRecipients);
+        AliasRecipient::create([
+            'alias' => $alias,
+            'recipient' => $recipientFive
+        ]);
 
-        $this->assertTrue($alias->hasNonPgpRecipients());
-        $this->assertCount(2, $pgpRecipients);
+        $this->assertCount(4, $alias->verifiedRecipientsOrDefault());
+        $this->assertCount(5, $alias->recipients);
     }
 }