diff --git a/app/Helpers/StringHelpers.php b/app/Helpers/StringHelpers.php index df23c51..dfb0cfa 100644 --- a/app/Helpers/StringHelpers.php +++ b/app/Helpers/StringHelpers.php @@ -62,3 +62,19 @@ function createAppKey() { return 'base64:' . base64_encode(Encrypter::generateKey(config('app.cipher'))); } + +function isHttps() +{ + if ( + (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] === 'on' || $_SERVER['HTTPS'] == 1)) || + (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') || + (isset($_SERVER['HTTP_X_FORWARDED_SCHEME']) && strtolower($_SERVER['HTTP_X_FORWARDED_SCHEME']) === 'https') || + (isset($_SERVER['HTTP_X_FORWARDED_SSL']) && ($_SERVER['HTTP_X_FORWARDED_SSL'] === 'on' || $_SERVER['HTTP_X_FORWARDED_SSL'] == 1)) || + (isset($_SERVER['REQUEST_SCHEME']) && strtolower($_SERVER['REQUEST_SCHEME']) === 'https') || + (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) + ) { + return true; + } + + return false; +} diff --git a/app/Http/Controllers/API/PageAPIController.php b/app/Http/Controllers/API/PageAPIController.php index cecc26a..6a34528 100644 --- a/app/Http/Controllers/API/PageAPIController.php +++ b/app/Http/Controllers/API/PageAPIController.php @@ -18,9 +18,10 @@ class PageAPIController extends Controller public function write(Request $request) { - $body = json_decode($request->getContent()); - $result = $this->pages->update($body); - return response()->json($result)->header('Content-Type', 'application/json'); + $body = json_decode($request->getContent()); + dd($body); + //$result = $this->pages->update($body); + //return response()->json($result)->header('Content-Type', 'application/json'); } public function create(Request $request) diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 1d491d3..3b23620 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -70,6 +70,6 @@ class Kernel extends HttpKernel 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'member.check' => \App\Http\Middleware\MemberCheck::class, - 'validate.token' => \App\Http\Middleware\ValidateAPIToken::class, + 'validate.key' => \App\Http\Middleware\ValidateAPIKey::class, ]; } diff --git a/app/Http/Middleware/ValidateAPIKey.php b/app/Http/Middleware/ValidateAPIKey.php new file mode 100644 index 0000000..7542024 --- /dev/null +++ b/app/Http/Middleware/ValidateAPIKey.php @@ -0,0 +1,66 @@ +member = $memberRepo; + $this->settings = $settingsService; + } + + /** + * Handle an incoming request. + * + * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next + */ + public function handle(Request $request, Closure $next): Response + { + $response = []; + //checks to see if request is secure + if (isHttps()) { + $key = $request->header('fipamo-api-key'); + $folks = $this->member->getAll(); + //looks to see if API key exists + if (find($folks, ['key' => $key])) { + //final check to see if API requests are being accepted + $global = $this->settings->getGlobal(); + if (isset($global['externalAPI']) && $global['externalAPI'] == "true") { + return $next($request); + } else { + $response = [ + 'message' => "API Auth Fail: Not Accepting Requests", + 'type' => 'postError', + ]; + return response()->json($response)->header('Content-Type', 'application/json'); + } + } else { + $response = [ + 'message' => "API Auth Fail: API Key Invalid", + 'type' => 'postError', + ]; + return response()->json($response)->header('Content-Type', 'application/json'); + } + } else { + $response = [ + 'message' => "API Auth Fail: Request must be secure (HTTPS)", + 'type' => 'postError', + ]; + return response()->json($response)->header('Content-Type', 'application/json'); + } + } +} diff --git a/app/Http/Middleware/ValidateAPIToken.php b/app/Http/Middleware/ValidateAPIToken.php deleted file mode 100644 index 92a328f..0000000 --- a/app/Http/Middleware/ValidateAPIToken.php +++ /dev/null @@ -1,29 +0,0 @@ -header('fipamo-access-token'); - if ($token == session('token')) { - return $next($request); - } else { - $response = [ - 'message' => "API Auth Fail", - 'type' => 'postError', - ]; - return response()->json($response)->header('Content-Type', 'application/json'); - } - } -} diff --git a/app/Repositories/MemberRepository.php b/app/Repositories/MemberRepository.php index 761e812..9c3b395 100644 --- a/app/Repositories/MemberRepository.php +++ b/app/Repositories/MemberRepository.php @@ -27,7 +27,7 @@ class MemberRepository implements MemberRepositoryInterface public function getAll() { - return $this->$folks; + return $this->folks; } public function getById($id) diff --git a/public/assets/scripts/dash/libraries/FipamoContentAPI.js b/public/assets/scripts/dash/libraries/FipamoContentAPI.js deleted file mode 100644 index e2c2ef6..0000000 --- a/public/assets/scripts/dash/libraries/FipamoContentAPI.js +++ /dev/null @@ -1,294 +0,0 @@ -//** REQUEST TYPES **// -export const REQUEST_TYPE_POST = "POST"; -export const REQUEST_TYPE_GET = "GET"; -export const REQUEST_TYPE_PUT = "PUT"; -export const REQUEST_TYPE_DELETE = "DELETE"; - -//** POST CONTENT TYPES **// -export const CONTENT_TYPE_JSON = "json"; -export const CONTENT_TYPE_FORM = "x-www-form-urlencoded"; - -//** API URLS **// -export const API_GET_PAGES = "/api/v1/page/published"; -export const API_GET_FEATURED = "/api/v1/page/featured"; -export const API_GET_PAGE = "/api/v1/page/single"; -export const API_GET_MENU = "/api/v1/page/menu"; -export const API_GET_TAGS = "/api/v1/page/tags"; - -//** API TASKS **// -export const TASK_GET_CONTENT = "retrieveContent"; - -/** - * A bag of methods for getting content from an install. - */ -class FipamoContentAPI { - /** - * @constructor - * @param {string} baseURL - url of install, defaults to local - * @param {string} key - user api key found in Settings - * @author Ro - */ - constructor(baseURL = null, key = null) { - this.baseURL = null; - this.key = null; - if (key) this.key = key; - if (baseURL) this.baseURL = baseURL; - } - - /** - * *Promise method for retrieving page data*\ - * **GET**`/api/v1/page/:type` - * @param {string} type - type of pages (`published | menu | featured`) being retrieved; null value defaults to `published` - * @example - * api.pages('published').then(pages=>{ - * console.log("Pages Object", pages); - * }) - * @returns {object} json object that contains pages of requested type - * - * *pages object example* - * ``` - { - "pages": - [ - { - "id":1, - "uuid":"uuid-for-entry", - "title":"Entry Title", - "feature":"/path/to/image.jpg", - "path":"2020/09", - "layout":"page", - "tags":"these, are, tags", - "author":"your-name", - "created":"2020 Sep Tue 01", - "updated":"2020 Sep Tue 01", - "deleted":false, - "menu":false, - "featured":false, - "published":true, - "slug":"entry-title", - "content":"Premium Content" - }, - { - "id":2, - "uuid":"uuid-for-entry", - "title":"Another Title", - "feature":"/path/to/image.jpg", - "path":"2020/09", - "layout":"page", - "tags":"these, are, tags", - "author":"your-name", - "created":"2020 Sep Tue 01", - "updated":"2020 Sep Tue 01", - "deleted":false, - "menu":false, - "featured":false, - "published":true, - "slug":"another-title", - "content":"Premium Content" - } - ], - "totalItems":2 - } - * ``` - * - */ - pages(type = null) { - //set url based on request type - let requestURL = ""; - switch (type) { - default: - case "published": - requestURL = API_GET_PAGES + "?key=" + this.key; - break; - case "featured": - requestURL = API_GET_FEATURED + "?key=" + this.key; - break; - case "menu": - requestURL = API_GET_MENU + "?key=" + this.key; - break; - } - return new Promise((resolve, reject) => { - this._request( - this.baseURL ? this.baseURL + requestURL : requestURL, - TASK_GET_CONTENT - ) - .then((result) => { - resolve(result); - }) - .catch((err) => { - reject(err); - }); - }); - } - /** - * *Promise method for retrieving single page*\ - * **GET** `/api/v1/page/single/:id` - * @param {string} id - uuid of desired page - * @example - * api.page("a-uuid-for-a-page").then(page=>{ - console.log("Page Object", page); - * }) - * @returns {object} json object that contains data for requested page - * - * *page object example* - * ``` - { - "id":1, - "uuid":"uuid-for-entry", - "title":"Entry Title", - "feature":"/path/to/image.jpg", - "path":"2020/09", - "layout":"page", - "tags":"these, are, tags", - "author":"your-name", - "created":"2020 Sep Tue 01", - "updated":"2020 Sep Tue 01", - "deleted":false, - "menu":false, - "featured":false, - "published":true, - "slug":"entry-title", - "content":"Premium Content" - } - * ``` - */ - page(id) { - return new Promise((resolve, reject) => { - this._request( - this.baseURL - ? this.baseURL + API_GET_PAGE + "/" + id + "?key=" + this.key - : API_GET_PAGE + "/" + id + "?key=" + this.key, - TASK_GET_CONTENT, - REQUEST_TYPE_GET - ) - .then((result) => { - resolve(result); - }) - .catch((err) => { - reject(err); - }); - }); - } - - /** - * *Promise method for retrieving all tags used by pages*\ - * **GET** `/api/v1/page/tags` - * @example - * api.tags().then(tags=>{ - console.log("Tags Object", tags); - * }) - * @returns {object} json object that contains site tags and page stubs associated with said tag - * - * *tags object example* - * ``` - [ - { - "tag_name":"this is a tag", - "slug":"this-is-a-tag", - "pages": - [ - { - "title":"This is a title", - "slug":"this-is-a-title", - "path":"2021/04" - }, - - { - "title":"This is another title", - "slug":"this-is-another-title", - "path":"2020/10" - } - ] - }, - { - "tag_name":"this is another tag", - "slug":"this-is-another-tag", - "pages": - [ - { - "title":"This is a title", - "slug":"this-is-a-title", - "path":"2021/04" - }, - - { - "title":"This is another title", - "slug":"this-is-another-title", - "path":"2020/10" - } - ] - } - ] - * ``` - */ - tags() { - return new Promise((resolve, reject) => { - this._request( - this.baseURL - ? this.baseURL + API_GET_TAGS + "?key=" + this.key - : API_GET_TAGS + "?key=" + this.key, - TASK_GET_CONTENT, - REQUEST_TYPE_GET - ) - .then((result) => { - resolve(result); - }) - .catch((err) => { - reject(err); - }); - }); - } - - //-------------------------- - // private - //-------------------------- - _request( - requestURL, - eventType, - requestType = REQUEST_TYPE_GET, - contentType = CONTENT_TYPE_JSON, - requestData = null - ) { - var self = this; - return new Promise(function (resolve, reject) { - var request = new XMLHttpRequest(); - request.upload.onprogress = self.handleLoadProgress; - request.open(requestType, requestURL, true); - request.onload = () => { - if (request.status == 200) { - let response = JSON.parse(request["response"]); - resolve(response); - } else { - let error = JSON.parse(request["response"]); - reject(error); - } - }; - if (requestType == REQUEST_TYPE_PUT || requestType == REQUEST_TYPE_POST) { - switch (contentType) { - case CONTENT_TYPE_JSON: - request.setRequestHeader( - "Content-type", - "application/" + contentType - ); - request.send(JSON.stringify(requestData)); - break; - case CONTENT_TYPE_FORM: - request.send(requestData); - break; - } - } else { - request.send(); - } - }); - } - - //-------------------------- - // event handlers - //-------------------------- - handleLoadProgress(e) { - this.percentComplete = Math.ceil((e.loaded / e.total) * 100); - //pass element to display request progress - } -} - -export { FipamoContentAPI as default }; diff --git a/routes/api.php b/routes/api.php index 64ae2ad..0543365 100644 --- a/routes/api.php +++ b/routes/api.php @@ -23,27 +23,27 @@ Route::post("/v1/init", [InitAPIController::class, 'setupFresh']); Route::post("/v1/restore", [InitAPIController::class, 'setupRestore']); //handle page editing actions -Route::group(['prefix' => '/v1/page', 'middleware' => 'validate.token'], function () { +Route::group(['prefix' => '/v1/page', 'middleware' => 'validate.key'], function () { Route::put("/write", [PageAPIController::class, 'write']); Route::post("/create", [PageAPIController::class, 'create']); Route::delete("/delete", [PageAPIController::class, 'delete']); }); //settings -Route::group(['prefix' => '/v1/settings', 'middleware' => 'validate.token'], function () { +Route::group(['prefix' => '/v1/settings', 'middleware' => 'validate.key'], function () { Route::put("/publish", [SettingsAPIController::class, 'publish']); Route::put("/sync", [SettingsAPIController::class, 'sync']); Route::put("/nav-sync", [SettingsAPIController::class, 'navSync']); }); //backups -Route::group(['prefix' => '/v1/backup', 'middleware' => 'validate.token'], function () { +Route::group(['prefix' => '/v1/backup', 'middleware' => 'validate.key'], function () { Route::put("/create", [SettingsAPIController::class, 'createBackup']); Route::get("/content-download", [SettingsAPIController::class, 'downloadBackup']); Route::get("/files-download", [SettingsAPIController::class, 'downloadBackup']); }); //other -Route::group(['prefix' => '/v1', 'middleware' => 'validate.token'], function () { +Route::group(['prefix' => '/v1', 'middleware' => 'validate.key'], function () { Route::post("/files", [FileUploadAPIController::class, 'upload']); Route::post("/reset", [InitAPIController::class, 'setupReset']); Route::post("/mailer", [MailAPIController::class, 'sendNotify']);