This commit is contained in:
kekskurse 2019-04-24 18:46:41 +00:00
commit d1d01dfb69
88 changed files with 7745 additions and 0 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2

19
.env.example Normal file
View File

@ -0,0 +1,19 @@
APP_NAME=Lumen
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_TIMEZONE=UTC
LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
CACHE_DRIVER=file
QUEUE_CONNECTION=sync

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/vendor
/.idea
Homestead.json
Homestead.yaml
.env

12
Dockerfile Normal file
View File

@ -0,0 +1,12 @@
FROM php:7.3-apache
RUN docker-php-ext-install mysqli
RUN docker-php-ext-install pdo pdo_mysql
RUN a2enmod rewrite
ADD ./ /var/www
RUN chmod uog+rwx /var/www/storage/logs
WORKDIR /var/www

19
Readme.md Normal file
View File

@ -0,0 +1,19 @@
#Keks Account
Keks Account is an oAuth2 Provider for a central User-Managment
#oAuth2
##API
Keks Account currently have no API
##GitLab
Keks Account contains a GitLab like oAuth2 and API to connect other Software to it.
# WebGui
Keks Account currently hava a cheap Hacked WebGui
# Supportet Software
* Mattermost CE (Gitlab Integration)
* Gitea (Gitlab Integration)
* Nextcloud (Gitlab Integration)
* Jenkins (Gitlab Integration)

View File

29
app/Console/Kernel.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Laravel\Lumen\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
//
}
}

9
app/Entity/Token.php Normal file
View File

@ -0,0 +1,9 @@
<?php
namespace App\Entity;
class Token
{
public $expires_at;
public $token;
public $refreshToken;
}

10
app/Events/Event.php Normal file
View File

@ -0,0 +1,10 @@
<?php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
abstract class Event
{
use SerializesModels;
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Events;
class ExampleEvent extends Event
{
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Exceptions;
class HTTPException extends \Exception
{
// Die Exception neu definieren, damit die Mitteilung nicht optional ist
public function __construct($httpCode, $message, $code = 0, Exception $previous = null) {
// etwas Code
// sicherstellen, dass alles korrekt zugewiesen wird
parent::__construct($message, $code, $previous);
}
// maßgeschneiderte Stringdarstellung des Objektes
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public function customFunction() {
echo "Eine eigene Funktion dieses Exceptiontyps\n";
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Laravel\Lumen\Routing\Controller as BaseController;
class Controller extends BaseController
{
//
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Http\Controllers;
class ExampleController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
//
}
//
}

View File

@ -0,0 +1,160 @@
<?php
namespace App\Http\Controllers\GUI;
use App\Exceptions\HTTPException;
use App\Http\Controllers\Controller;
use App\Http\Resources\oAuth\AccessToken;
use App\Jobs\Mails\ValidateMailAddressJob;
use App\Models\App;
use App\Models\AppAccess;
use App\Models\Invite;
use App\Models\Mail;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class AccountController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
//
}
public function registerView(Request $request) {
$invite = Invite::query()->where("code", "=", $request->input("invite"))->first();
if($invite != null) {
if($invite->status != "active") {
return view('error', ["msg"=>"Invite code invalide"]);
}
return view('account/register', ["msg"=>"", "username" => $invite->username, "invite"=>$invite->code]);
}
$setting = Setting::query()->where("name", "=", "registration_possible")->firstOrFail();
if(!$setting->value) {
return view('error', ["msg"=>"Registration is disabled"]);
} else {
return view('account/register', ["msg"=>"", "username" => "", "invite"=>$request->input("invite")]);
}
}
public function register(Request $request) {
$this->validate($request, [
'username' => 'required|max:255|min:5|regex:@^[a-z0-9]*$@|unique:users',
'password' => 'required|min:8',
'mail' => 'required|email|unique:mails'
]);
DB::beginTransaction();
$invite = Invite::query()->where("code", "=", $request->input("invite"))->first();
if($invite != null) {
if($invite->status != "active") {
throw new HTTPException("Invite code invalide");
}
if(!empty($invite->username) && $request->input("username") != $invite->username) {
throw new HTTPException("Invalide username for invite");
}
} else {
$setting = Setting::query()->where("name", "=", "registration_possible")->firstOrFail();
if(!$setting->value) {
throw new HTTPException("400", "Registration disabled");
}
}
$user = new User();
$user->username = $request->input("username");
$user->password = password_hash($request->input("password"), PASSWORD_BCRYPT);
//Make first user an admin
$count = User::query()->count("*");
if($count == 1) {
$user->admin = 1;
$user->developer = 1;
}
$user->saveOrFail();
$mail = new Mail();
$mail->createValidationToken();
$mail->mail = $request->input("mail");
$mail->primary = false;
$mail->status = "waiting";
$mail->user_id = $user->id;
$mail->saveOrFail();
$this->dispatch(new ValidateMailAddressJob($mail));
if($invite != null) {
$invite->status = "used";
$invite->saveOrFail();
}
DB::commit();
}
public function inviteView() {
return view('account/invite_code', ["msg"=>""]);
}
public function loginView() {
return view('account/login', ["msg"=>""]);
}
public function login(Request $request) {
$this->validate($request, [
'username' => 'required',
'password' => 'required'
]);
$user = User::query()->where("username", "=", $request->input("username"))->first();
if($user==null) {
abort(401, "Username or Password wrong");
}
if(!password_verify($request->input("password"), $user->password)) {
abort(401, "Username or Password wrong");
}
$access = AppAccess::getOrCreate($user->id, App::query()->where("name", "=", "PHP-GUI")->firstOrFail()->id);
$token = \App\Models\AccessToken::createToken($access);
$_SESSION["token"] = $token->token;
return new AccessToken($token);
}
public function logout() {
session_destroy();
return view('account/login', ["msg"=>"Logout successful", "user" => null]);
}
public function validateEMail($id, $code) {
$mail = Mail::query()->where("id", "=", $id)->firstOrFail();
if($mail->validation_code != $code) {
throw new \App\Exceptions\HTTPException(400, "Wrong validation code");
}
$mail->status = "valide";
$mail->primary = true;
$mails = Mail::query()->where("user_id", "=", $mail->user_id)->where("primary", "=", true)->get("*");
foreach($mails as $m) {
$m->primary = false;
$m->saveOrFail();
}
$mail->saveOrFail();
echo "E-Mail wurde validiert";
}
//
}

View File

@ -0,0 +1,137 @@
<?php
namespace App\Http\Controllers\GUI;
use App\Exceptions\HTTPException;
use App\Http\Controllers\Controller;
use App\Http\Resources\oAuth\AccessToken;
use App\Models\App;
use App\Models\Invite;
use App\Models\Mail;
use App\Models\Setting;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AdminController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
//
}
public function listMails() {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$mails = Mail::query()->get("*");
return view('admin/mails_list', ["mails"=>$mails]);
}
public function inviteView() {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$invites = Invite::query()->get();
return view('admin/invites_list', ["invites"=>$invites]);
}
public function inviteNewView() {
return view('admin/invites_new', []);
}
public function settingsView() {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$settings = Setting::query()->get("*");
return view('admin/settings_list', ["settings"=>$settings]);
}
public function saveSettings(Request $request) {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$settings = Setting::query()->get("*");
foreach($settings as $setting) {
switch ($setting->typ) {
case "checkbox":
if($request->input($setting->name, false)) {
$setting->value = true;
} else {
$setting->value = false;
}
$setting->saveOrFail();
case "textinput":
$setting->value = $request->input($setting->name, "");
$setting->saveOrFail();
case "password":
$setting->value = $request->input($setting->name, "");
$setting->saveOrFail();
}
}
$settings = Setting::query()->get("*");
return view('admin/settings_list', ["settings"=>$settings]);
}
public function appList() {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$apps = App::query()->get("*");
return view('admin/app_list', ["msg"=>"", "apps" => $apps]);
}
public function appDetails($id) {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$app = App::query()->where("id", "=", $id)->first("*");
return view('admin/app_details', ["msg"=>"", "app" => $app]);
}
public function appDetailsSave(Request $request, $id) {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$app = App::query()->where("id", "=", $id)->first("*");
$app->auto_accept = (bool)$request->input("auto_accept", false);
$app->testing_warning = (bool)$request->input("testing_warning", false);
$app->untrusted_warning = (bool)$request->input("untrusted_warning", false);
$app->show_on_webpage = (bool)$request->input("show_on_webpage", false);
$app->saveOrFail();
$app = App::query()->where("id", "=", $id)->first("*");
return view('admin/app_details', ["msg"=>"", "app" => $app]);
}
public function inviteNew(Request $request) {
if(!Auth::user()->admin) {
throw new HTTPException("Need Admin Access");
}
$invite = new Invite();
$invite->user_id = Auth::user()->id;
$invite->username = $request->input("username", null);
$invite->comment = $request->input("comment", null);
$invite->status = "active";
$invite->createToken();
$invite->saveOrFail();
return redirect("/gui/admin/invites");
}
//
}

View File

@ -0,0 +1,91 @@
<?php
namespace App\Http\Controllers\GUI;
use App\Http\Controllers\Controller;
use App\Http\Resources\oAuth\AccessToken;
use App\Models\App;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AppController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
//
}
public function appList() {
$apps = App::query()->where("user_id", "=", Auth::user()->id)->get();
return view('app/list', ["msg"=>"", "apps" => $apps]);
}
public function newAppView() {
return view('app/new', ["msg"=>""]);
}
public function newApp(Request $request) {
$this->validate($request, [
'name' => 'required|max:255|min:3|regex:@^[a-zA-Z0-9]*$@|unique:apps',
'description' => 'required|min:3',
'url' => 'required|url'
]);
$app = App::createApp($request->input("name"), $request->input("description"), $request->input("url"), Auth::user());
return "App created";
}
public function viewApp(Request $request, $id) {
$app = App::query()->where("id", "=", $id)->first();
if($app->user_id != Auth::user()->id) {
abort(401);
}
return view('app/details', ["msg"=>"", "app" => $app]);
}
public function changeIcon(Request $request, $id) {
// Todo: Replace prove of concept with better code
$newTmp = tempnam("", "icon_upload");
$request->file("icon")->move("/tmp", $newTmp);
$info = getimagesize($newTmp);
if($info["0"] != $info["1"]) {
echo "Icon is not a squader";exit();
}
if($info[0] > 500) {
echo "Icon is to big, max 500 px";exit();
}
if($info[0] < 50) {
echo "Icon is to small, min 50px";exit();
}
$app = App::query()->where("id", "=", $id)->firstOrFail();
if($app->user_id != Auth::user()->id) {
echo "Its not your app";exit();
}
$app->icon = file_get_contents($newTmp);
$app->saveOrFail();
echo "OK";
}
public function getAppIcon($id) {
$app = App::query()->where("id", "=", $id)->firstOrFail();
$r = getimagesizefromstring($app->icon);
return response($app->icon)
->header('Content-Type',$r["mime"]);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers\GUI;
use App\Http\Controllers\Controller;
use App\Models\App;
use Illuminate\Support\Facades\Log;
class PublicController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function index()
{
$apps = App::query()->where("show_on_webpage", "=", 1)->get();
Log::debug("TEST");
return view('index', ["msg"=>"", "apps" => $apps]);
}
//
}

View File

@ -0,0 +1,159 @@
<?php
namespace App\Http\Controllers;
use App\Entity\Token;
use App\Exceptions\HTTPException;
use App\Models\AccessToken;
use App\Models\App;
use App\Models\AppAccess;
use App\Models\AppCode;
use App\Models\RefreshToken;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class oAuthController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
//
}
public function authorizeView(Request $request) {
$app = App::query()->where("apiKey", "=", $request->input("client_id"))->first();
$user = Auth::user();
$redirect = false;
if($user != null) {
$access = AppAccess::getOrCreate($user->id, $app->id);
if($access->status == "allowed") {
$redirect = true;
}
if($app->auto_accept == true) {
$access->status = "allowed";
$access->saveOrFail();
$redirect = true;
}
// @ToDo: if access is already granted redirect direct without ask user again
}
if($redirect) {
$returnUrl = urldecode($request->input("redirect_uri"));
if(strpos($returnUrl, "?") > 0) {
Log::debug("Found questionmark in redirect_uri");
if(substr($returnUrl, -1, 1) != "&") {
Log::debug("Add & to the redirect_uri");
$returnUrl .= "&";
}
} else {
$returnUrl .= "?";
}
$appCode = AppCode::createCode($access);
$returnUrl.="code=".$appCode->code."&state=".$request->input("state");
Log::debug("Return URL: ".$returnUrl);
return redirect($returnUrl);
}
return view('oAuth/authorizeLogin', ["msg"=>"", "app"=>$app]);
}
public function authorizeDo(Request $request)
{
//Check user
$user = Auth::user();
$app = App::query()->where("apiKey", "=", $request->input("client_id"))->first();
//Check if user send username/password if not logged in
if(is_null($user)) {
$user = User::query()->where("username", "=", $request->input("username"))->first();
if($user == null){
return view('oAuth/authorizeLogin', ["msg"=>"Username or Password wrong", "app"=>$app]);
}
if(!password_verify($request->input("password"), $user->password)) {
return view('oAuth/authorizeLogin', ["msg"=>"Username or Password wrong", "app"=>$app]);
}
}
$access = AppAccess::getOrCreate($user->id, $app->id);
$access->status = "allowed";
$access->saveOrFail();
$returnUrl = urldecode($request->input("redirect_uri"));
if(strpos($returnUrl, "?") > 0) {
Log::debug("Found questionmark in redirect_uri");
if(substr($returnUrl, -1, 1) != "&") {
Log::debug("Add & to the redirect_uri");
$returnUrl .= "&";
}
} else {
$returnUrl .= "?";
}
$appCode = AppCode::createCode($access);
$returnUrl.="code=".$appCode->code."&state=".$request->input("state");
Log::debug("Return URL: ".$returnUrl);
return redirect($returnUrl);
}
public function token(Request $request) {
$code = AppCode::query()->where("code", "=", $request->input("code"))->first();
$access = $code->getAccess();
if($access->status != "allowed") {
throw new HTTPException("401", "Code has no access");
}
$accessToken = AccessToken::createToken($access);
$refreshToken = RefreshToken::createToken($access);
$token = new Token();
$token->expires_at = $accessToken->expires_at;
$token->token = $accessToken->token;
$token->refreshToken = $refreshToken->token;
$d = [
'access_token' => $accessToken->token,
'token_type' => 'bearer',
'expires_in' => strtotime($accessToken->expires_at)-time(),
'refresh_token' => $refreshToken->token
];
echo json_encode($d);
}
public function getUserTMP() {
$user = Auth::user();
$data = [];
$data["id"] = $user->id;
$data["username"] = $user->username;
$data["sub"] = $user->username;
$data["email"] = $user->getMail();
$data["name"] = $user->username;
$data["state"] = "active";
$data["avatar_url"] = "https://www.alzforum.org/sites/default/files/member-default.jpg";
#$data["web_url"] = "http://www.kekskurse.de";
$data["is_admin"] = false;
$data["public_email"] = $user->getMail();
echo json_encode($data);
}
public function getGroupsTMP() {
echo "[]";
}
//
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate
{
/**
* The authentication guard factory instance.
*
* @var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Auth\Factory $auth
* @return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
return response('Unauthorized.', 401);
}
return $next($request);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Closure;
class ExampleMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
return $next($request);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use App\Models\Setting;
use Closure;
use Illuminate\Support\Facades\Auth;
class View
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
view()->share('user', Auth::user());
view()->share('settingsArray', Setting::getSettingsAsArray());
return $next($request);
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class oAuthMiddelware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
view()->share('user', Auth::user());
return $next($request);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources\oAuth;
use Illuminate\Http\Resources\Json\JsonResource;
class AccessToken extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'access_token' => $this->token,
'token_type' => 'bearer',
'expires_in' => strtotime($this->expires_at)-time(),
];
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Resources\oAuth;
use Illuminate\Http\Resources\Json\JsonResource;
class Token extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'access_token' => $this->token,
'token_type' => 'bearer',
'expires_in' => strtotime($this->expires_at)-time(),
'refresh_token' => $this->refreshToken
];
}
}

26
app/Jobs/ExampleJob.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace App\Jobs;
class ExampleJob extends Job
{
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}

24
app/Jobs/Job.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
abstract class Job implements ShouldQueue
{
/*
|--------------------------------------------------------------------------
| Queueable Jobs
|--------------------------------------------------------------------------
|
| This job base class provides a central location to place any logic that
| is shared across all of your jobs. The trait included with the class
| provides access to the "queueOn" and "delay" queue helper methods.
|
*/
use InteractsWithQueue, Queueable, SerializesModels;
}

View File

@ -0,0 +1,53 @@
<?php
namespace App\Jobs\Mails;
use App\Jobs\Job;
use App\Models\Mail;
use App\Models\Setting;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
class ValidateMailAddressJob extends Job
{
private $mailObject = null;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Mail $mailObject)
{
$this->mailObject = $mailObject;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if(Setting::getSettingValue("smtp_active") == false) {
return;
}
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = Setting::getSettingValue("smtp_host");
$mail->SMTPAuth = Setting::getSettingValue("smtp_smtpAuth");
$mail->Username = Setting::getSettingValue("smtp_username");
$mail->Password = Setting::getSettingValue("smtp_password");
$mail->SMTPSecure = Setting::getSettingValue("smtp_secure");
$mail->Port = Setting::getSettingValue("smtp_port");
$mail->setFrom(Setting::getSettingValue("smtp_from_mail"), Setting::getSettingValue("smtp_from_name"));
$mail->addAddress($this->mailObject->mail);
$bcc = Setting::getSettingValue("smtp_bcc");
if(!empty($bcc)) {
$mail->addBCC($bcc);
}
$mail->isHTML(true);
$mail->Subject = 'Keks Account E-Mail validation';
$mail->Body = 'Hello,<br>to validate your E-Mail address click on the following link: <a href="'.Setting::getSettingValue('url').'/gui/mailValidation/'.$this->mailObject->id.'/'.$this->mailObject->validation_code.'">Activate Account</a>';
$mail->AltBody = 'Hello,\r\nto validate your E-Mail address click on the following link: '.Setting::getSettingValue('url').'/gui/mailValidation/'.$this->mailObject->id.'/'.$this->mailObject->validation_code;
$mail->send();
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Listeners;
use App\Events\ExampleEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ExampleListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param ExampleEvent $event
* @return void
*/
public function handle(ExampleEvent $event)
{
//
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class AccessToken extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'status', 'expires_at', 'access_id', 'token'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'',
];
static function createToken(AppAccess $access) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring = $characters[rand(0, strlen($characters)-1)];
}
$token = hash("sha512", $access->id.time().$randstring);
$accessToken = new AccessToken();
$accessToken->expires_at = date("Y-m-d H:i:s", time()+3600);
$accessToken->access_id = $access->id;
$accessToken->token = $token;
$accessToken->saveOrFail();
return $accessToken;
}
public function getAppAccess(): AppAccess {
return AppAccess::query()->where("id", "=", $this->access_id)->firstOrFail();
}
public function getUser(): User {
return User::query()->where("id", "=", $this->getAppAccess()->user_id)->firstOrFail();
}
}

61
app/Models/App.php Normal file
View File

@ -0,0 +1,61 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class App extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'description', 'url', 'apiKey', 'apiSecret', 'auto_accept', 'testing_warning', 'untrusted_warning', 'user_id'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'',
];
static function createApp($name, $description, $url, User $user) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring = $characters[rand(0, strlen($characters)-1)];
}
$apiKey = hash("sha512", $randstring);
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring = $characters[rand(0, strlen($characters)-1)];
}
$apiSecret = hash("sha512", $randstring);
$app = new App();
$app->name = $name;
$app->description = $description;
$app->url = $url;
$app->apiKey = $apiKey;
$app->apiSecret = $apiSecret;
$app->auto_accept = false;
$app->testing_warning = true;
$app->untrusted_warning = false;
$app->user_id = $user->id;
$app->saveOrFail();
return $app;
}
}

72
app/Models/AppAccess.php Normal file
View File

@ -0,0 +1,72 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class AppAccess extends Model
{
protected $table = 'app_access';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'app_id', 'user_id', 'status_id'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'',
];
static function createCode(User $user, App $app) {
$characters = '0123456789';
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring .= $characters[rand(0, strlen($characters)-1)];
}
$code = $randstring;
$appCode = new AppCode();
$appCode->user_id = $user->id;
$appCode->app_id = $app->id;
$appCode->code = $code;
$appCode->saveOrFail();
return $appCode;
}
public function getUser() {
return User::query()->where("id", "=", $this->user_id)->first();
}
public function getApp() {
return App::query()->where("id", "=", $this->app_id)->first();
}
static public function getOrCreate($userId, $appId) {
$access = AppAccess::query()->where("user_id", "=", $userId)->where("app_id", "=", $appId)->first();
if($access == null) {
$access = new AppAccess();
$access->user_id = $userId;
$access->app_id = $appId;
$access->status = "created";
$access->saveOrFail();
}
return $access;
}
}

51
app/Models/AppCode.php Normal file
View File

@ -0,0 +1,51 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class AppCode extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'access_id', 'code'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'',
];
static function createCode(AppAccess $appAccess) {
$characters = '0123456789';
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring .= $characters[rand(0, strlen($characters)-1)];
}
$code = $randstring;
$appCode = new AppCode();
$appCode->access_id = $appAccess->id;
$appCode->code = $code;
$appCode->saveOrFail();
return $appCode;
}
public function getAccess(): AppAccess {
return AppAccess::query()->where("id", "=", $this->access_id)->firstOrFail();
}
}

40
app/Models/Invite.php Normal file
View File

@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class Invite extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id', 'username', 'code', 'comment', 'status'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'validation_code',
];
public function createToken() {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring .= $characters[rand(0, strlen($characters)-1)];
}
$this->code = $randstring;
}
}

40
app/Models/Mail.php Normal file
View File

@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class Mail extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id', 'mail', 'validation_code', 'primary', 'status'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'validation_code',
];
public function createValidationToken() {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring .= $characters[rand(0, strlen($characters)-1)];
}
$this->validation_code = $randstring;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class RefreshToken extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'status', 'user_id', 'app_id', 'token'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'',
];
static function createToken(AppAccess $access) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < 20; $i++) {
$randstring .= $characters[rand(0, strlen($characters)-1)];
}
$token = hash("sha512", $access->id.time().$randstring);
$accessToken = new AccessToken();
$accessToken->access_id = $access->id;
$accessToken->token = $token;
$accessToken->saveOrFail();
return $accessToken;
}
public function getUser() {
return User::query()->where("id", "=", $this->user_id)->first();
}
}

44
app/Models/Setting.php Normal file
View File

@ -0,0 +1,44 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class Setting extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'description', 'typ', 'value'
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'',
];
static public function getSettingsAsArray() {
$settings = Setting::query()->get();
$settingsArray = [];
foreach($settings as $s) {
$settingsArray[$s->name] = $s->value;
}
return $settingsArray;
}
static public function getSettingValue($name) {
return Setting::query()->where("name", "=", $name)->firstOrFail()->value;
}
}

45
app/Models/User.php Normal file
View File

@ -0,0 +1,45 @@
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
class User extends Model implements AuthenticatableContract, AuthorizableContract
{
use Authenticatable, Authorizable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'username',
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'password',
];
public function getMail() {
$mail = Mail::query()
->where("user_id", "=", $this->id)
->where("primary", "=", 1)
->where("status", "=", "valide")
->first();
if(is_null($mail)) {
return null;
}
return $mail->mail;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace App\Providers;
use App\Models\AccessToken;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Boot the authentication services for the application.
*
* @return void
*/
public function boot()
{
// Here you may define how you wish users to be authenticated for your Lumen
// application. The callback which receives the incoming request instance
// should return either a User instance or null. You're free to obtain
// the User instance via an API token or any other method necessary.
$this->app['auth']->viaRequest('api', function (Request $request) {
$token = null;
if(isset($_SESSION["token"])) {
$token = $_SESSION["token"];
}
if(isset($_GET["access_token"])) {
$token = $_GET["access_token"];
}
if(isset($_GET["token"])) {
$token = $_GET["token"];
}
if($request->header("Authorization", false)) {
$token = trim($request->header("Authorization"));
$t = explode(" ", $token);
$token = last($t);
}
if($token == null) {
return null;
}
$accessToken = AccessToken::query()->where("token", "=", $token)->first();
if(time() > strtotime($accessToken->expires_at)) {
return null;
}
if($accessToken->status =! "active") {
return null;
}
return $accessToken->getUser();
});
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Providers;
use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'App\Events\ExampleEvent' => [
'App\Listeners\ExampleListener',
],
];
}

35
artisan Normal file
View File

@ -0,0 +1,35 @@
#!/usr/bin/env php
<?php
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| First we need to get an application instance. This creates an instance
| of the application / container and bootstraps the application so it
| is ready to receive HTTP / Console requests from the environment.
|
*/
$app = require __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(
'Illuminate\Contracts\Console\Kernel'
);
exit($kernel->handle(new ArgvInput, new ConsoleOutput));

101
bootstrap/app.php Normal file
View File

@ -0,0 +1,101 @@
<?php
require_once __DIR__.'/../vendor/autoload.php';
(new Laravel\Lumen\Bootstrap\LoadEnvironmentVariables(
dirname(__DIR__)
))->bootstrap();
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/
$app = new Laravel\Lumen\Application(
dirname(__DIR__)
);
$app->withFacades();
$app->withEloquent();
/*
|--------------------------------------------------------------------------
| Register Container Bindings
|--------------------------------------------------------------------------
|
| Now we will register a few bindings in the service container. We will
| register the exception handler and the console kernel. You may add
| your own bindings here if you like or you can make another file.
|
*/
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
/*
|--------------------------------------------------------------------------
| Register Middleware
|--------------------------------------------------------------------------
|
| Next, we will register the middleware with the application. These can
| be global middleware that run before and after each request into a
| route or middleware that'll be assigned to some specific routes.
|
*/
// $app->middleware([
// App\Http\Middleware\ExampleMiddleware::class
// ]);
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
'gui' => \App\Http\Middleware\View::class
]);
/*
|--------------------------------------------------------------------------
| Register Service Providers
|--------------------------------------------------------------------------
|
| Here we will register all of the application's service providers which
| are used to bind services into the container. Service providers are
| totally optional, so you are not required to uncomment this line.
|
*/
// $app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
// $app->register(App\Providers\EventServiceProvider::class);
/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/
$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});
return $app;

4
build.sh Normal file
View File

@ -0,0 +1,4 @@
rm -r -f storage/logs/l*
chmod uog+rwx storage/logs
docker build -t docker.keks.cloud/keksaccount/web:latest .
docker push docker.keks.cloud/keksaccount/web:latest

44
composer.json Normal file
View File

@ -0,0 +1,44 @@
{
"name": "laravel/lumen",
"description": "The Laravel Lumen Framework.",
"keywords": ["framework", "laravel", "lumen"],
"license": "MIT",
"type": "project",
"require": {
"php": ">=7.1.3",
"laravel/lumen-framework": "5.8.*",
"vlucas/phpdotenv": "^3.3",
"phpmailer/phpmailer": "~6.0"
},
"require-dev": {
"fzaninotto/faker": "^1.4",
"phpunit/phpunit": "^7.0",
"mockery/mockery": "^1.0"
},
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/"
}
},
"autoload-dev": {
"classmap": [
"tests/"
]
},
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
]
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

4423
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
<?php
/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| Here you may define all of your model factories. Model factories give
| you a convenient way to create models for testing and seeding your
| database. Just tell the factory how a default model should look.
|
*/
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
];
});

View File

View File

@ -0,0 +1,125 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class Init extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('username');
$table->string('password');
$table->boolean('developer')->default(false);
$table->boolean('admin')->default(false);
$table->enum('status', ['active']);
});
Schema::create("mails", function(Blueprint $table) {
$table->increments("id");
$table->timestamps();
$table->unsignedInteger("user_id");
$table->string("mail");
$table->string("validation_code");
$table->boolean("primary")->comment("This is the Account primary mail adress");
$table->enum("status", ["waiting", "valide", "invalide"]);
$table->foreign('user_id')->references('id')->on('users');
$table->unique(['user_id', 'primary'], 'unique_primary_email');
});
Schema::create('apps', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->unsignedInteger('user_id');
$table->string("name");
$table->text("description");
$table->string("url");
$table->string("apiKey");
$table->string("apiSecret");
$table->enum("trustLevel", ["untrustet", "trustet", "full-trustet"])->comment("If level is untrustet a warning is shown, trustet show a notice, only full-trustet it without warning");
$table->enum("owner", ["user", "system"])->comment("If owner is user a warning is shown");
$table->enum("status", ["active", "deactivate", "testing"])->default("active");
$table->foreign('user_id')->references('id')->on('users');
});
Schema::create("app_codes", function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->unsignedInteger('app_id');
$table->unsignedInteger('user_id');
$table->string('code');
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('app_id')->references('id')->on('apps');
});
Schema::create('access_tokens', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->enum('status', ['active'])->default('active');
$table->timestamp('expires_at')->nullable();
$table->unsignedInteger('user_id');
$table->unsignedInteger('app_id')->nullable();
$table->string('token');
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('app_id')->references('id')->on('apps');
});
Schema::create('refresh_tokens', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->enum('status', ['active'])->default('active');
$table->unsignedInteger('user_id');
$table->unsignedInteger('app_id')->nullable();
$table->string('token');
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('app_id')->references('id')->on('apps');
});
Schema::create('settings', function(Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string("name");
$table->string("description");
$table->enum("typ", ["checkbox"]);
$table->string("value");
});
$setting = new \App\Models\Setting();
$setting->name = "registration_possible";
$setting->description = "Can new user create an account";
$setting->typ = "checkbox";
$setting->value = 1;
$setting->saveOrFail();
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop("settings");
Schema::drop('refresh_tokens');
Schema::drop('access_tokens');
Schema::drop('app_codes');
Schema::drop('apps');
Schema::drop('mails');
Schema::drop('users');
}
}

View File

@ -0,0 +1,46 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class Invite extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$setting = new \App\Models\Setting();
$setting->name = "invites";
$setting->description = "Enabled Invites";
$setting->typ = "checkbox";
$setting->value = 1;
$setting->saveOrFail();
Schema::create('invites', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('username')->nullable()->default(null)->comment("If set the username can't be changed");
$table->string("user_id")->comment("User who create the invite");
$table->string("code")->comment("Invite code");
$table->string("comment")->nullable()->default(null);
$table->enum("status", ["active", "inaktive", "used"])->default("active");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$s = \App\Models\Setting::query()->where("name", "=", "invites")->first();
$s->delete();
Schema::drop('invites');
}
}

View File

@ -0,0 +1,88 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class NewAppConfig extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('app_access', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->unsignedInteger('user_id');
$table->unsignedInteger('app_id');
$table->enum("status", ["created", "allowed", "disabled"]);
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('app_id')->references('id')->on('apps');
});
Schema::table('apps', function (Blueprint $table) {
$table->boolean('auto_accept')->default(false)->comment('User dont have to click login, if the user is logged in he/she will immediately redirected back.');
$table->boolean('testing_warning')->default(true)->comment('Show warning that the Application is just in testing mode');
$table->boolean('untrusted_warning')->default(false)->comment('Show warning that the Application is untrusted');
$table->dropColumn('trustLevel');
$table->dropColumn('owner');
$table->dropColumn('status');
});
\App\Models\AppCode::query()->delete();
Schema::table('app_codes', function (Blueprint $table) {
$table->dropForeign("app_codes_app_id_foreign");
$table->dropColumn("app_id");
$table->dropForeign("app_codes_user_id_foreign");
$table->dropColumn("user_id");
$table->unsignedInteger("access_id");
$table->foreign('access_id')->references('id')->on('app_access');
});
\App\Models\AccessToken::query()->delete();
Schema::table('access_tokens', function (Blueprint $table) {
$table->dropForeign("access_tokens_app_id_foreign");
$table->dropColumn("app_id");
$table->dropForeign("access_tokens_user_id_foreign");
$table->dropColumn("user_id");
$table->unsignedInteger("access_id");
$table->foreign('access_id')->references('id')->on('app_access');
});
\App\Models\RefreshToken::query()->delete();
Schema::table('refresh_tokens', function (Blueprint $table) {
$table->dropForeign("refresh_tokens_app_id_foreign");
$table->dropColumn("app_id");
$table->dropForeign("refresh_tokens_user_id_foreign");
$table->dropColumn("user_id");
$table->unsignedInteger("access_id");
$table->foreign('access_id')->references('id')->on('app_access');
});
$user = new \App\Models\User();
$user->username = "system";
$user->password = "";
$user->admin = 0;
$user->developer = 0;
$user->saveOrFail();
$app = \App\Models\App::createApp("PHP-GUI", "Webgui for oAuth Provider", "https://account.keks.cloud", $user);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop("app_access");
}
}

View File

@ -0,0 +1,115 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class MailAndAppSettings extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
DB::statement("ALTER TABLE settings MODIFY COLUMN typ ENUM('checkbox', 'textinput', 'password')");
$setting = new \App\Models\Setting();
$setting->name = "smtp_active";
$setting->description = "If SMTP is not active the system will not send any mails";
$setting->typ = "checkbox";
$setting->value = 0;
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_host";
$setting->description = "SMTP Host";
$setting->typ = "textinput";
$setting->value = "";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_port";
$setting->description = "SMTP Port";
$setting->typ = "textinput";
$setting->value = "587";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_smtpAuth";
$setting->description = "Use SMTP-Auth";
$setting->typ = "checkbox";
$setting->value = 1;
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_username";
$setting->description = "SMTP Username";
$setting->typ = "textinput";
$setting->value = "";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_password";
$setting->description = "SMTP Password";
$setting->typ = "password";
$setting->value = "";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_secure";
$setting->description = "SMTP Secure Message";
$setting->typ = "textinput";
$setting->value = "tls";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_from_mail";
$setting->description = "SMTP From Mail";
$setting->typ = "textinput";
$setting->value = "account@example.com";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_from_name";
$setting->description = "SMTP From Name";
$setting->typ = "textinput";
$setting->value = "KeksAccount";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "smtp_bcc";
$setting->description = "BCC All Messages to this address (No BCC fi empty)";
$setting->typ = "textinput";
$setting->value = "";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "url";
$setting->description = "URL of this Service";
$setting->typ = "textinput";
$setting->value = "https://account.keks.cloud";
$setting->saveOrFail();
$setting = new \App\Models\Setting();
$setting->name = "name";
$setting->description = "Name of this Service";
$setting->typ = "textinput";
$setting->value = "Keks Account";
$setting->saveOrFail();
Schema::table('apps', function (Blueprint $table) {
$table->boolean('show_on_webpage')->default(false)->comment('If this flag is true, the App will shown on the Webpage');
$table->string("direct_url")->nullable()->default(null)->comment("Direct Login URL");
$table->binary("icon")->nullable()->default(null)->comment("200x200 Image as Icon");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$settings = \App\Models\Setting::query()->where("name", "LIKE", "smtp_%")->get();
foreach($settings as $s) {
$s->delete();
}
\App\Models\Setting::query()->where("name", "=", "url")->delete();
DB::statement("ALTER TABLE settings MODIFY COLUMN typ ENUM('checkbox')");
}
}

View File

@ -0,0 +1,16 @@
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// $this->call('UsersTableSeeder');
}
}

17
docker-compose.yml Normal file
View File

@ -0,0 +1,17 @@
version: '3'
services:
webapp:
build: ./
volumes:
- ./:/var/www
ports:
- 8000:80
mysql:
image: mysql:5.7
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: oauth
MYSQL_USER: oauth
MYSQL_PASSWORD: oauth

1
html Symbolic link
View File

@ -0,0 +1 @@
public

147
kube.yml Normal file
View File

@ -0,0 +1,147 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: env-keksaccount-web
namespace: keksaccount
data:
APP_DEBUG: "false"
DB_CONNECTION: "mysql"
DB_HOST: "mysql"
DB_PORT: "3306"
DB_PORT: "oauth"
DB_USERNAME: "oauth"
DB_PASSWORD: "oauth"
---
apiVersion: v1
kind: Secret
metadata:
name: secret-keksaccount-mysql
namespace: keksaccount
type: Opaque
data:
MYSQL_ROOT_PASSWORD: dGlmaW1hZG9ja2Vy
MYSQL_DATABASE: b2F1dGg=
MYSQL_USER: b2F1dGg=
MYSQL_PASSWORD: b2F1dGg=
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: keksaccount
namespace: keksaccount
spec:
replicas: 1
template:
metadata:
labels:
app: keksaccount
spec:
containers:
- name: web
image: docker.keks.cloud/keksaccount/web:latest
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: env-keksaccount-web
imagePullSecrets:
- name: docker-keks-cloud
---
apiVersion: v1
kind: Service
metadata:
annotations:
field.cattle.io/targetWorkloadIds: '["deployment:keksaccount:keksaccount"]'
name: ingress-keksaccount
namespace: keksaccount
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
type: ClusterIP
status:
loadBalancer: {}
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mysql
namespace: keksaccount
spec:
replicas: 1
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.6
ports:
- containerPort: 3306
envFrom:
- secretRef:
name: secret-keksaccount-mysql
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql
volumes:
- name: mysql
persistentVolumeClaim:
claimName: mysql
---
apiVersion: v1
kind: Service
metadata:
annotations:
field.cattle.io/targetWorkloadIds: '["deployment:keksaccount:mysql"]'
name: mysql
namespace: keksaccount
spec:
ports:
- port: 3306
protocol: TCP
targetPort: 3306
type: ClusterIP
status:
loadBalancer: {}
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: account.keks.cloud
namespace: keksaccount
spec:
secretName: account-keks-cloud-tls
acme:
config:
- dns01:
provider: cf-dns
domains:
- 'account.keks.cloud'
commonName: 'account.keks.cloud'
dnsNames:
- account.keks.cloud
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: keksaccount
namespace: keksaccount
spec:
rules:
- host: account.keks.cloud
http:
paths:
- backend:
serviceName: ingress-keksaccount
servicePort: 80
path: /
tls:
- secretName: account-keks-cloud-tls

26
phpunit.xml Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="bootstrap/app.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Application Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
</php>
</phpunit>

21
public/.htaccess Normal file
View File

@ -0,0 +1,21 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>

1
public/html Symbolic link
View File

@ -0,0 +1 @@
html

BIN
public/img/logo3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

29
public/index.php Normal file
View File

@ -0,0 +1,29 @@
<?php
session_start();
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| First we need to get an application instance. This creates an instance
| of the application / container and bootstraps the application so it
| is ready to receive HTTP / Console requests from the environment.
|
*/
$app = require __DIR__.'/../bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/
$app->run();

0
resources/views/.gitkeep Normal file
View File

View File

@ -0,0 +1,19 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>Invite Code</h3>
<?php if(!empty($msg)) { ?>
<div class="alert alert-warning" role="alert">
<?php echo $msg; ?>
</div>
<?php } ?>
<form method="get" action="/gui/register" id="login">
<b>Invite Code</b>
<input class="form-control" name="invite">
<br>
<input type="submit" value="Check" class="btn btn-success">
</form>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,46 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>Register</h3>
<?php if(!empty($msg)) { ?>
<div class="alert alert-warning" role="alert">
<?php echo $msg; ?>
</div>
<?php } ?>
<form method="post" id="login">
<b>Username:</b> <span id="msg_username"></span>
<input name="username" placeholder="Username" class="form-control">
<b>Password:</b> <span id="msg_password"></span>
<input name="password" type="password" placeholder="Password" class="form-control">
<input type="submit" class="btn btn-success" value="Login" style="margin-top: 10px;">
</form>
</div>
</div>
<script language="JavaScript">
$(document).ready(function () {
console.log("READY");
$("#login").submit(function (e) {
e.preventDefault();
var form = $(this);
$.ajax({
type: "POST",
url: "/gui/login",
data: form.serialize(), // serializes the form's elements.
success: function (data) {
window.location.href = "/";
},
error: function (data) {
if(data.status == 422) {
$.each(data.responseJSON, function( key, value ) {
$("#msg_"+key).html(value[0]);
});
}
if(data.status == 401) {
alert("Usernamme/Password falsch");
}
}
});
});
});
</script>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,65 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>Register</h3>
<?php if(!empty($msg)) { ?>
<div class="alert alert-warning" role="alert">
<?php echo $msg; ?>
</div>
<?php } ?>
<form method="post" id="register">
<input name="invite" placeholder="Username" class="form-control" value="<?php echo $invite; ?>" style="display: none;">
<b>Username:</b> <span id="msg_username" class="msg"></span>
<?php
if(!empty($username)) {
?>
<input name="username" placeholder="Username" class="form-control" value="<?php echo $username; ?>" style="display: none;">
<input class="form-control" value="<?php echo $username; ?>" disabled>
<?php
} else {
?>
<input name="username" placeholder="Username" class="form-control" value="<?php echo $username; ?>">
<?php
}
?>
<b>Password:</b> <span id="msg_password" class="msg"></span>
<input name="password" type="password" placeholder="Password" class="form-control">
<b>E-Mail</b> <span id="msg_mail" class="mail"></span>
<input type="email" placeholder="E-Mail" name="mail" class="form-control">
<input type="submit" class="btn btn-success" value="Register" style="margin-top: 10px;">
</form>
</div>
</div>
<script language="JavaScript">
$(document).ready(function () {
console.log("READY");
$("#register").submit(function (e) {
e.preventDefault();
var form = $(this);
$.ajax({
type: "POST",
url: "/gui/register",
data: form.serialize(), // serializes the form's elements.
success: function (data) {
window.location.href = "/gui/login";
},
error: function (data) {
$(".msg").each(function (key, e) {
console.log(e);
$(e).html("");
})
if(data.status == 422) {
$.each(data.responseJSON, function( key, value ) {
$("#msg_"+key).html(value[0]);
});
}
if(data.status == 401) {
alert("Usernamme/Password falsch");
}
}
});
});
});
</script>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,78 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>Apps <?php echo $app->name; ?></h3>
<p><?php echo $app->description; ?></p>
<a href="<?php echo $app->url; ?>"><?php echo $app->url; ?></a>
<hr>
<h2>Settings</h2>
<form method="post">
<table class="table">
<tr>
<th>Name</th>
<th>Beschreibung</th>
<th>Value</th>
</tr>
<!--<tr>
<td>Status</td>
<td colspan="2">
<select name="status" class="form-control">
<option <?php if($app->status == "active" ) { echo "selected='selected'"; } ?>>active</option>
<option <?php if($app->status == "testing" ) { echo "selected='selected'"; } ?>>testing</option>
<option <?php if($app->status == "deactivate" ) { echo "selected='selected'"; } ?>>deactivate</option>
</select>
</td>
</tr>
<tr>
<td>Trust Level</td>
<td colspan="2">
<select name="trustLevel" class="form-control">
<option <?php if($app->trustLevel == "full-trustet" ) { echo "selected='selected'"; } ?>>full-trustet</option>
<option <?php if($app->trustLevel == "trustet" ) { echo "selected='selected'"; } ?>>trustet</option>
<option <?php if($app->trustLevel == "untrustet" ) { echo "selected='selected'"; } ?>>untrustet</option>
</select>
</td>
</tr>
<tr>
<td>Owner</td>
<td colspan="2">
<select name="owner" class="form-control">
<option <?php if($app->owner == "user" ) { echo "selected='selected'"; } ?>>user</option>
<option <?php if($app->owner == "system" ) { echo "selected='selected'"; } ?>>system</option>
</select>
</td>
</tr>!-->
<tr>
<td>Auto Accept</td>
<td>User dont have to click login, if the user is logged in he/she will immediately redirected back.</td>
<td>
<input name="auto_accept" type="checkbox" <?php if($app->auto_accept) { echo 'checked="checked"'; } ?>>
</td>
</tr>
<tr>
<td>Testing Warning</td>
<td>Show warning that the Application is just in testing mode</td>
<td>
<input name="testing_warning" type="checkbox" <?php if($app->testing_warning) { echo 'checked="checked"'; } ?>>
</td>
</tr>
<tr>
<td>Untrusted Warning</td>
<td>Show warning that the Application is untrusted</td>
<td>
<input name="untrusted_warning" type="checkbox" <?php if($app->untrusted_warning) { echo 'checked="checked"'; } ?>>
</td>
</tr>
<tr>
<td>Show on Webpage</td>
<td>Show this App on the Webpage.</td>
<td>
<input name="show_on_webpage" type="checkbox" <?php if($app->show_on_webpage) { echo 'checked="checked"'; } ?>>
</td>
</tr>
</table>
<input type="submit" class="btn btn-warning" value="Save">
</form>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,20 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>Apps</h3>
<?php
if(empty($apps)) {
echo '<i>Keine Apps gefunden</i>';
} else {
?>
<div class="list-group" id="list-tab" role="tablist">
<?php foreach($apps as $app) { ?>
<a class="list-group-item list-group-item-action" id="list-profile-list" href="/gui/admin/apps/<?php echo $app->id; ?>"><?php echo $app->name; ?></a>
<?php } ?>
</div>
<?php
}
?>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,28 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<a href="/gui/admin/invites/new" class="btn btn-success" style="float: right;">New Invite</a>
<h3>Invite Token</h3>
<table class="table">
<tr>
<th>ID</th>
<th>Username</th>
<th>Status</th>
<th>Invide Token</th>
<th>Comment</th>
</tr>
<?php
foreach($invites as $invite) {
echo "<tr>";
echo "<td>".$invite->id."</td>";
echo "<td>".$invite->username."</td>";
echo "<td>".$invite->status."</td>";
echo "<td>".$invite->code."</td>";
echo "<td>".$invite->comment."</td>";
echo "</tr>";
}
?>
</table>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,15 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>New Invite</h3>
<form method="post">
<b>Username*</b>
<input name="username" class="form-control">
<b>Description</b>
<input name="comment" class="form-control"><br>
<input type="submit" class="btn btn-success" value="Create">
</form>
<p>* if empty the user can choose it own username</p>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,32 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>E-Mail adresses</h3>
<table class="table">
<tr>
<th>User ID</th>
<th>Mail</th>
<th>Actions</th>
</tr>
<?php
foreach($mails as $mail) {
?>
<tr>
<td><?php echo $mail->user_id; ?></td>
<td><?php echo $mail->mail; ?></td>
<td>
<?php
if($mail->status == "waiting") {
echo '<a href="/gui/mailValidation/'.$mail->id.'/'.$mail->validation_code.'" class="btn btn-danger btn-sm">Aktivieren</a>';
}
?>
</td>
</tr>
<?php
}
?>
</table>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,34 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>Settings</h3>
<form method="post">
<?php
foreach($settings as $setting) {
$button = false;
echo '<h4>'.$setting->name.'</h4>';
echo '<p>'.$setting->description.'</p>';
switch ($setting->typ) {
case 'checkbox':
echo '<input type="checkbox" name="'.$setting->name.'"';
if($setting->value) {
echo' checked="checked"';
}
echo '>'.$setting->name.'<br>';
$button = true;
break;
case 'textinput':
echo '<input class="form-control" name="'.$setting->name.'" value="'.$setting->value.'">';
break;
case 'password':
echo '<input class="form-control" type="password" name="'.$setting->name.'" value="'.$setting->value.'">';
break;
}
echo "<hr>";
}
?>
<input type="submit" value="Save" class="btn btn-success">
</form>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,23 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>App <?php echo $app->name; ?></h3>
<p><?php echo $app->description; ?></p>
<a href="<?php echo $app->url; ?>"><?php echo $app->url; ?></a>
<hr>
<h3>Settings</h3>
<b>Icon</b>
You can change the Icon of the App by uploading a square Pic.
<form action="/gui/apps/<?php echo $app->id; ?>/changeIcon" method="post" enctype="multipart/form-data">
<input type="file" name="icon" class="form-control" ><br>
<input type="submit" class="btn btn-success" value="Upload">
</form>
<hr>
<h3>API Access</h3>
<b>API-Key</b>
<input class="form-control" value="<?php echo $app->apiKey; ?>">
<b>API-Secret</b>
<input class="form-control" value="<?php echo $app->apiSecret; ?>">
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,21 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<a href="/gui/apps/new" class="btn btn-success" style="float:right;">Neue App</a>
<h3>Apps</h3>
<?php
if(empty($apps)) {
echo '<i>Keine Apps gefunden</i>';
} else {
?>
<div class="list-group" id="list-tab" role="tablist">
<?php foreach($apps as $app) { ?>
<a class="list-group-item list-group-item-action" id="list-profile-list" href="/gui/apps/<?php echo $app->id; ?>"><?php echo $app->name; ?></a>
<?php } ?>
</div>
<?php
}
?>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,16 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>New App</h3>
<form method="post">
<b>Name</b>
<input class="form-control" name="name" placeholder="Name">
<b>Description</b>
<textarea class="form-control" name="description" placeholder="App Beschreibung"></textarea>
<b>URL</b>
<input class="form-control" name="url" placeholder="https://wwww.appurl.com">
<input type="submit" class="btn btn-success" style="margin-top:20px;" value="Create App">
</form>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,8 @@
<?php include("layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<h3>Error</h3>
<p><?php echo $msg; ?></p>
</div>
</div>
<?php include("layout/bottom.php"); ?>

61
resources/views/index.php Normal file
View File

@ -0,0 +1,61 @@
<?php include("layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
<div class="alert alert-light" role="alert">
account.keks.cloud is still in development.
</div>
</div>
</div>
<div class="row" style="display: none;">
<div class="col-md-12">
<p>Register to use the keks.cloud services. Keks Account currently works with the following Services:
<ul>
<li><a href="">Gitea</a></li>
<li><del>Wekan</del></li>
<li><del>Chat</del></li>
<li><del>Webmail / Mail adress</del></li>
</ul>
You can create your own App using the Keks-Account oAuth Provider too.
</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>Keks Account</h3>
<p>
account.keks.cloud is a oAuht Provider to login with a single user to different Services. You just need one Account to use all Services, also other developers can add this Service to there services.
</p>
<p>
In the test phase of the Project you need an Invite-Code to create a new Account.
</p>
</div>
</div>
<?php
if(count($apps) > 0) {
?>
<div class="row">
<div class="col-md-12">
<h3>Login to</h3>
</div>
</div>
<div class="row">
<?php
foreach($apps as $app) {
?>
<div class="col-md-4" style="margin-top:10px;">
<a href="<?php echo $app->direct_url; ?>">
<div class="card">
<div class="card-body" style="padding-top:0px;padding-bottom:0px;">
<img src="/gui/apps/<?php echo $app->id; ?>/icon" style="width: 50px;margin-right:40px;margin-top:5px;margin-bottom:5px;"><?php echo $app->name; ?>
</div>
</div>
</a>
</div>
<?php } ?>
</div>
<?php } ?>
<?php include("layout/bottom.php"); ?>

View File

@ -0,0 +1,12 @@
</div>
<footer class="page-footer font-small blue">
<!-- Copyright -->
<div class="footer-copyright text-center py-3">Make with <i class="fas fa-cookie"></i> for <a href="https://www.keks.cloud">keks.cloud</a>
- <i class="fas fa-code-branch"></i> <a href="https://gitea.keks.cloud/keks.cloud/keksAccount">Keks Account</a>
</div>
<!-- Copyright -->
</footer>
</body>
</html>

View File

@ -0,0 +1,81 @@
<html>
<head>
<title>Keks Account</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://bootswatch.com/4/flatly/bootstrap.min.css" type="text/css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
</head>
<body>
<div class="container" style="margin-top: 30px;min-height: calc(100vh - 100px);">
<nav class="navbar navbar-expand-lg navbar-dark bg-primary" style="margin-bottom: 20px">
<a class="navbar-brand" href="/">Keks Account</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor01">
<ul class="navbar-nav mr-auto">
<!--<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>!-->
<!--<li class="nav-item">
<a class="nav-link" href="#">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Privacy</a>
</li>!-->
</ul>
<?php if(is_null($user)) { ?>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="/gui/login">Login</a>
</li>
<?php if($settingsArray["registration_possible"]) { ?>
<li class="nav-item">
<a class="nav-link" href="/gui/register">Register</a>
</li>
<?php } ?>
</ul>
<?php } else { ?>
<ul class="navbar-nav ml-auto">
<?php if($user->developer) { ?>
<li class="nav-item">
<a class="nav-link" href="/gui/apps">Apps</a>
</li>
<?php } ?>
<?php if($user->admin) { ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Admin
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="/gui/admin/settings">Settings</a>
<a class="dropdown-item disabled" href="#">Users</a>
<a class="dropdown-item" href="/gui/admin/apps">Apps</a>
<?php if($settingsArray["invites"]) {
echo '<a class="dropdown-item" href="/gui/admin/invites">Invites</a>';
}?>
<a class="dropdown-item" href="/gui/admin/mails">E-Mail Adressen</a>
</div>
</li>
<?php } ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<?php echo $user->username; ?>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item disabled" href="#">Profile</a>
<a class="dropdown-item disabled" href="#">Privacy</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="/gui/logout">Logout</a>
</div>
</li>
</ul>
<?php } ?>
</div>
</nav>

View File

@ -0,0 +1,55 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
</div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="login">
<h3>Login to App <?php echo $app->name; ?></h3>
<p><?php echo $app->description; ?></p>
<hr>
<?php
if($app->trustLevel == "untrustet") {
?>
<div class="alert alert-danger">
<h4 class="alert-heading">Warning!</h4>
<p class="mb-0">You login to an untrusted App.</p>
</div>
<?php
}
?>
<?php
if($app->owner == "user" && $app->trustLevel != "full-trustet") {
?>
<div class="alert alert-info">
<h4 class="alert-heading">Information!</h4>
<p class="mb-0">You login to an Application from another User, this Application will see your user Information.</p>
</div>
<?php
}
?>
<?php
if($app->status == "testing") {
?>
<div class="alert alert-warning">
<h4 class="alert-heading">Testing!</h4>
<p class="mb-0">This is a testing Application.</p>
</div>
<?php
}
?>
<form method="post">
<input type="submit" class="btn btn-success" value="Login to TEST" style="width: 100%;margin-top: 10px;">
</form>
<br>
<a href="<?php echo $app->url; ?>">Zur Webseite der App</a>
</div>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,61 @@
<?php include(__DIR__."/../layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
</div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="login">
<h3>Login to App <?php echo $app->name; ?></h3>
<p><?php echo $app->description; ?></p>
<hr>
<?php
if($app->untrusted_warning) {
?>
<div class="alert alert-danger">
<h4 class="alert-heading">Warning!</h4>
<p class="mb-0">You login to an untrusted App.</p>
</div>
<?php
}
?>
<?php
if($app->testing_warning) {
?>
<div class="alert alert-warning">
<h4 class="alert-heading">Testing!</h4>
<p class="mb-0">This is a testing Application.</p>
</div>
<?php
}
?>
<hr>
<?php if(!empty($msg)) { ?>
<div class="alert alert-warning">
<p class="mb-0"><?php echo $msg; ?></p>
</div>
<?php } ?>
<form method="post">
<?php
if(is_null($user)) {
?>
<b>Username</b>
<input name="username" class="form-control">
<b>Password</b>
<input name="password" type="password" class="form-control">
<?php
}
?>
<input type="submit" class="btn btn-success" value="Login to <?php echo $app->name; ?>" style="width: 100%;margin-top: 10px;">
</form>
<br>
<a href="<?php echo $app->url; ?>">Zur Webseite der App</a>
</div>
</div>
</div>
<?php include(__DIR__."/../layout/bottom.php"); ?>

View File

@ -0,0 +1,31 @@
<?php include("layout/top.php"); ?>
<div class="row">
<div class="col-md-12">
</div>
</div>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="login">
<h3>Login to App TEST</h3>
<hr>
<div class="alert alert-dismissible alert-warning">
<h4 class="alert-heading">Warning!</h4>
<p class="mb-0">This is an external App, this App may will save your user informations and use it, please check the privacy information from the Service.</p>
</div>
Die App TEST bekommt zugriff auf folgende Datein:
<ul>
<li>Username</li>
<li>E-Mail adresse</li>
</ul>
<input type="button" class="btn btn-success" value="Login to TEST" style="width: 100%;">
</div>
</div>
</div>
<?php include("layout/bottom.php"); ?>

64
routes/web.php Normal file
View File

@ -0,0 +1,64 @@
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It is a breeze. Simply tell Lumen the URIs it should respond to
| and give it the Closure to call when that URI is requested.
|
*/
$router->get('/', ['middleware' => 'gui', 'uses' => 'GUI\PublicController@index']);
$router->get('/api/v4/user', ['uses' => 'oAuthController@getUserTMP']);
$router->get('/api/v4/groups', ['uses' => 'oAuthController@getGroupsTMP']);
$router->group(['prefix' => 'gui', 'middleware' => 'gui'], function () use ($router) {
$router->get('/register', ['uses' => 'GUI\AccountController@registerView']);
$router->post('/register', ['uses' => 'GUI\AccountController@register']);
$router->get('/invite', ['uses' => 'GUI\AccountController@inviteView']);
$router->get('/login', ['uses' => 'GUI\AccountController@loginView']);
$router->get('/logout', ['uses' => 'GUI\AccountController@logout']);
$router->post('/login', ['uses' => 'GUI\AccountController@login']);
$router->get('/apps', ['uses' => 'GUI\AppController@appList']);
$router->get('/apps/new', ['uses' => 'GUI\AppController@newAppView']);
$router->post('/apps/new', ['uses' => 'GUI\AppController@newApp']);
$router->get('/apps/{id}', ['uses' => 'GUI\AppController@viewApp']);
$router->post('/apps/{id}/changeIcon', ['uses' => 'GUI\AppController@changeIcon']);
$router->get('/apps/{id}/icon', ['uses' => 'GUI\AppController@getAppIcon']);
$router->get("/mailValidation/{id}/{code}", ['uses' => 'GUI\AccountController@validateEMail']);
//Admin
$router->get("/admin/mails", ['uses' => 'GUI\AdminController@listMails']);
$router->get("/admin/settings", ['uses' => 'GUI\AdminController@settingsView']);
$router->post("/admin/settings", ["uses" => "GUI\AdminController@saveSettings"]);
$router->get("/admin/apps", ["uses" => "GUI\AdminController@appList"]);
$router->get("/admin/apps/{id}", ["uses" => "GUI\AdminController@appDetails"]);
$router->post("/admin/apps/{id}", ["uses" => "GUI\AdminController@appDetailsSave"]);
$router->get("/admin/invites", ["uses" => "GUI\AdminController@inviteView"]);
$router->get("/admin/invites/new", ["uses" => "GUI\AdminController@inviteNewView"]);
$router->post("/admin/invites/new", ["uses" => "GUI\AdminController@inviteNew"]);
$router->get('user/profile', function () {
// Uses Auth Middleware
});
});
/*$router->group(['prefix' => 'api'], function () use ($router) {
$router->group(['prefix' => 'auth'], function () use ($router) {
$router->post('/register', ['uses' => 'GUI\AccountController@register']);
});
});*/
$router->group(['prefix' => 'oauth'], function () use ($router) {
$router->get("/authorize", ['middleware' => 'gui', 'uses' => 'oAuthController@authorizeView']);
$router->post("/authorize", ['middleware' => 'gui', 'uses' => 'oAuthController@authorizeDo']);
$router->post("/token", ['uses' => 'oAuthController@token']);
});

2
storage/app/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/views/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/logs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

21
tests/ExampleTest.php Normal file
View File

@ -0,0 +1,21 @@
<?php
use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testExample()
{
$this->get('/');
$this->assertEquals(
$this->app->version(), $this->response->getContent()
);
}
}

14
tests/TestCase.php Normal file
View File

@ -0,0 +1,14 @@
<?php
abstract class TestCase extends Laravel\Lumen\Testing\TestCase
{
/**
* Creates the application.
*
* @return \Laravel\Lumen\Application
*/
public function createApplication()
{
return require __DIR__.'/../bootstrap/app.php';
}
}