Adam Pippin
3 years ago
26 changed files with 6367 additions and 32 deletions
@ -0,0 +1,13 @@ |
|||||
|
root = true |
||||
|
|
||||
|
[*] |
||||
|
charset = utf-8 |
||||
|
end_of_line = lf |
||||
|
indent_style = tab |
||||
|
|
||||
|
[*.md] |
||||
|
trim_trailing_whitespace = false |
||||
|
|
||||
|
[*.yml] |
||||
|
indent_style = space |
||||
|
indent_size = 2 |
@ -0,0 +1,2 @@ |
|||||
|
/vendor |
||||
|
/.php_cs.cache |
@ -0,0 +1,64 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
return [ |
||||
|
'target_php_version' => '7.4', |
||||
|
|
||||
|
'directory_list' => [ |
||||
|
'.phan/stubs/', |
||||
|
'src/', |
||||
|
'vendor/laravel/', |
||||
|
'vendor/composer/', |
||||
|
'vendor/symfony/' |
||||
|
], |
||||
|
|
||||
|
'exclude_analysis_directory_list' => [ |
||||
|
'vendor/', |
||||
|
'.phan/stubs/' |
||||
|
], |
||||
|
|
||||
|
'plugins' => [ |
||||
|
// stricter checks on whether a function returns the proper type |
||||
|
'AlwaysReturnPlugin', |
||||
|
// warn about trying to set duplicate keys in arrays (common mistake) |
||||
|
// warn about duplicate switch case values |
||||
|
// warn about mixing arrays/dictionaries |
||||
|
'DuplicateArrayKeyPlugin', |
||||
|
// validate regular expressions |
||||
|
'PregRegexCheckerPlugin', |
||||
|
// validate printf statements |
||||
|
'PrintfCheckerPlugin', |
||||
|
// Check for unreachable code in functions |
||||
|
'UnreachableCodePlugin', |
||||
|
// warn if you're calling a function where you probably should be using |
||||
|
// the return value (e.g., printf) but are not |
||||
|
'UseReturnValuePlugin', |
||||
|
// Infer values from phpunit tests |
||||
|
//'PHPUnitAssertionPlugin' |
||||
|
// Warn if structures are missing phpdoc plugins |
||||
|
'HasPHPDocPlugin', |
||||
|
// warn on isset(func()['index']) and isset($array[$key]) where $array is not set |
||||
|
//'InvalidVariableIssetPlugin' |
||||
|
// warn about returning non-array values from __sleep |
||||
|
'SleepCheckerPlugin', |
||||
|
// warn about unknown return/parameter type (not documented and unable to |
||||
|
// be inferred) |
||||
|
'UnknownElementTypePlugin', |
||||
|
// warn about expressions that are likely to be a bug (e.g., a == a) |
||||
|
'DuplicateExpressionPlugin', |
||||
|
// try and use some heuristics to detect if parameters are out of order |
||||
|
// on some function calls |
||||
|
//'SuspiciousParamOrderPlugin', |
||||
|
|
||||
|
], |
||||
|
|
||||
|
'plugin_config' => [ |
||||
|
// UseReturnValuePlugin -- slow; check and see if the return value of a |
||||
|
// function is *normally* used and if so, warn where it is not. |
||||
|
//'use_return_value_dynamic_checks'=>true |
||||
|
//'use_return_value_warn_threshold_percentage'=>98, |
||||
|
], |
||||
|
|
||||
|
'minimum_severity' => 0 |
||||
|
]; |
@ -0,0 +1,330 @@ |
|||||
|
<?php |
||||
|
|
||||
|
$finder = Symfony\Component\Finder\Finder::create() |
||||
|
->exclude('bootstrap/cache') |
||||
|
->exclude('storage') |
||||
|
->exclude('vendor') |
||||
|
->in(__DIR__) |
||||
|
->name('*.php') |
||||
|
->ignoreDotFiles(true) |
||||
|
->ignoreVCS(true) |
||||
|
; |
||||
|
|
||||
|
return PhpCsFixer\Config::create() |
||||
|
->setRules([ |
||||
|
'align_multiline_comment' => [ |
||||
|
'comment_type' => 'phpdocs_only' |
||||
|
], |
||||
|
'array_indentation' => true, |
||||
|
'array_syntax' => [ |
||||
|
'syntax' => 'short' |
||||
|
], |
||||
|
'binary_operator_spaces' => [ |
||||
|
'default' => 'single_space' |
||||
|
], |
||||
|
'blank_line_after_namespace' => true, |
||||
|
'blank_line_after_opening_tag' => true, |
||||
|
//'blank_line_before_statement' => [ |
||||
|
// 'statements' => ['break', 'case', 'continue', 'declare', 'default', 'die', 'do', 'exit', 'for', 'foreach', 'goto', 'if', 'include', 'include_once', 'require', 'require_once', 'return', 'switch', 'throw', 'try', 'while', 'yield'] |
||||
|
// ] |
||||
|
'braces' => [ |
||||
|
'allow_single_line_closure' => true, |
||||
|
'position_after_anonymous_constructs' => 'same', |
||||
|
'position_after_control_structures' => 'next', |
||||
|
'position_after_functions_and_oop_constructs' => 'next' |
||||
|
], |
||||
|
'cast_spaces' => [ |
||||
|
'space' => 'none' |
||||
|
], |
||||
|
'class_attributes_separation' => [ |
||||
|
'elements' => ['const', 'method', 'property'] |
||||
|
], |
||||
|
'class_definition' => [ |
||||
|
'multi_line_extends_each_single_line' => false, |
||||
|
'single_item_single_line' => false, |
||||
|
'single_line' => false |
||||
|
], |
||||
|
'class_keyword_remove' => false, |
||||
|
'combine_consecutive_issets' => false, |
||||
|
'combine_consecutive_unsets' => true, |
||||
|
'combine_nested_dirname' => true, |
||||
|
'comment_to_phpdoc' => true, |
||||
|
'compact_nullable_typehint' => true, |
||||
|
'concat_space' => [ |
||||
|
'spacing' => 'none' |
||||
|
], |
||||
|
//'date_time_immutable' => false, |
||||
|
'declare_equal_normalize' => [ |
||||
|
'space'=>'none' |
||||
|
], |
||||
|
'declare_strict_types' => true, |
||||
|
'dir_constant' => true, |
||||
|
'elseif' => true, |
||||
|
'encoding' => true, |
||||
|
'ereg_to_preg' => true, |
||||
|
'error_suppression' => [ |
||||
|
'mute_deprecation_error' => false, |
||||
|
'noise_remaining_usages' => true, |
||||
|
'noise_remaining_usages_exclude' => [] // functions to exclude |
||||
|
], |
||||
|
'escape_implicit_backslashes' => [ |
||||
|
'double_quoted' => true, |
||||
|
'heredoc_syntax' => true, |
||||
|
'single_quoted' => true |
||||
|
], |
||||
|
'explicit_indirect_variable' => true, |
||||
|
'explicit_string_variable' => false, |
||||
|
//'final_class' => true, // mark all non-abstract classes as final |
||||
|
//'final_internal_class' => [ see doc for opts ] // mark all internal classes as final |
||||
|
'fopen_flag_order' => true, |
||||
|
'fopen_flags' => [ |
||||
|
'b_mode' => true |
||||
|
], |
||||
|
'full_opening_tag' => true, |
||||
|
//'fully_qualified_strict_types' => ???, |
||||
|
'function_declaration' => [ |
||||
|
'closure_function_spacing' => 'none' |
||||
|
], |
||||
|
//'function_to_constant' => [ |
||||
|
// replaces get_called_class, get_class, php_sapi_name, phpversion, pi with |
||||
|
// constants... not sure how that could be valid? |
||||
|
//], |
||||
|
'function_typehint_space' => true, |
||||
|
'general_phpdoc_annotation_remove' => [ |
||||
|
'annotations' => [] // list of @annotations to remove form phpdoc comments |
||||
|
], |
||||
|
//'heredoc_indentation' => true, // requires php 7.3 |
||||
|
'heredoc_to_nowdoc' => true, |
||||
|
'implode_call' => true, |
||||
|
'include' => true, |
||||
|
//'increment_style' => [ |
||||
|
// 'style' => // post/pre |
||||
|
//], |
||||
|
'indentation_type' => true, |
||||
|
'is_null' => true, |
||||
|
'line_ending' => true, |
||||
|
'linebreak_after_opening_tag' => true, |
||||
|
'list_syntax' => [ |
||||
|
'syntax' => 'short' |
||||
|
], |
||||
|
'logical_operators' => true, |
||||
|
'lowercase_cast' => true, |
||||
|
'lowercase_constants' => true, |
||||
|
'lowercase_keywords' => true, |
||||
|
'lowercase_static_reference' => true, |
||||
|
'magic_constant_casing' => true, |
||||
|
'magic_method_casing' => true, |
||||
|
//'mb_str_function' => true, // replace non-mb safe with mb_ methods |
||||
|
'method_argument_space' => [ |
||||
|
'keep_multiple_spaces_after_comma' => false, |
||||
|
'on_multiline' => 'ignore' // ensure_fully_multiline, ensure_single_line, ignore |
||||
|
], |
||||
|
'method_chaining_indentation' => true, |
||||
|
'modernize_types_casting' => true, |
||||
|
'multiline_comment_opening_closing' => true, |
||||
|
'multiline_whitespace_before_semicolons' => [ |
||||
|
'strategy' => 'new_line_for_chained_calls' |
||||
|
], |
||||
|
'native_constant_invocation' => [], |
||||
|
'native_function_casing' => true, |
||||
|
'native_function_invocation' => [], |
||||
|
'native_function_type_declaration_casing' => true, |
||||
|
//'new_with_brances' => true, // no idea what this does? included in @Symfony bundle |
||||
|
'no_alias_functions' => [], |
||||
|
'no_alternative_syntax' => true, // get rid of while {} endwhile; stuff |
||||
|
'no_binary_string' => true, |
||||
|
'no_blank_lines_after_class_opening' => true, |
||||
|
'no_blank_lines_after_phpdoc' => true, |
||||
|
//'no_blank_lines_before_namespace' => true, |
||||
|
'no_break_comment' => [ |
||||
|
'comment_text' => 'no break' |
||||
|
], |
||||
|
'no_closing_tag' => true, |
||||
|
'no_empty_comment' => true, |
||||
|
'no_empty_phpdoc' => true, |
||||
|
'no_empty_statement' => true, |
||||
|
'no_extra_blank_lines' => [ |
||||
|
'tokens' => ['extra'] |
||||
|
], |
||||
|
'no_homoglyph_names' => true, |
||||
|
'no_leading_import_slash' => true, |
||||
|
'no_leading_namespace_whitespace' => true, |
||||
|
'no_mixed_echo_print' => [ |
||||
|
'use' => 'echo' |
||||
|
], |
||||
|
'no_multiline_whitespace_around_double_arrow' => true, |
||||
|
'no_null_property_initialization' => false, |
||||
|
//'no_php4_constructor' => true, |
||||
|
'no_short_bool_cast' => true, |
||||
|
'no_short_echo_tag' => false, |
||||
|
'no_singleline_whitespace_before_semicolons' => true, |
||||
|
'no_spaces_after_function_name' => true, |
||||
|
'no_spaces_around_offset' => [ |
||||
|
'positions' => ['inside', 'outside'] |
||||
|
], |
||||
|
'no_spaces_inside_parenthesis' => true, |
||||
|
//'no_superfluous_elseif' => false, // don't know what this would do? |
||||
|
//'no_superfluous_phpdoc_tags' => [ |
||||
|
// removes @param/@return that "don't provide any useful information" |
||||
|
//], |
||||
|
'no_trailing_comma_in_list_call' => true, |
||||
|
'no_trailing_comma_in_singleline_array' => true, |
||||
|
'no_trailing_whitespace' => true, |
||||
|
'no_trailing_whitespace_in_comment' => true, |
||||
|
'no_unneeded_control_parentheses' => true, |
||||
|
'no_unneeded_curly_braces' => true, |
||||
|
'no_unneeded_final_method' => true, |
||||
|
'no_unreachable_default_argument_value' => true, |
||||
|
'no_unset_cast' => true, |
||||
|
'no_unset_on_property' => false, |
||||
|
'no_unused_imports' => true, |
||||
|
'no_useless_else' => true, |
||||
|
'no_useless_return' => true, |
||||
|
'no_whitespace_before_comma_in_array' => [ |
||||
|
'after_heredoc' => false |
||||
|
], |
||||
|
'no_whitespace_in_blank_line' => true, |
||||
|
'non_printable_character' => [ |
||||
|
'use_escape_sequences_in_strings' => true // don't just remove them, make them visible to the programmer and let them sort it out |
||||
|
], |
||||
|
'normalize_index_brace' => true, |
||||
|
'not_operator_with_space' => false, |
||||
|
'not_operator_with_successor_space' => false, |
||||
|
'object_operator_without_whitespace' => true, |
||||
|
//'ordered_class_elements' => [ // lets us sort methods/props/etc in classes |
||||
|
// 'order' => [], |
||||
|
// 'sortAlgorithm'=>'' |
||||
|
//], |
||||
|
//'ordered_imports' => [ // sort use statements, off so we can retain some sort of context |
||||
|
// 'sort_algorithm' => 'alpha' |
||||
|
//], |
||||
|
//'ordered_interfaces' => [ // sort implements or interface extends |
||||
|
//], |
||||
|
'php_unit_construct' => [ // replace phpunit's ->assertSame(true, $foo) with ->assertTrue($foo) |
||||
|
], |
||||
|
'php_unit_dedicate_assert' => [ // try and replace things like assertTrue(file_exists()) with assertFileExists |
||||
|
'target'=>'newest' |
||||
|
], |
||||
|
'php_unit_dedicate_assert_internal_type' => [ |
||||
|
'target'=>'newest' |
||||
|
], |
||||
|
'php_unit_expectation' => [ // should use expectException instead of setExpectedException |
||||
|
'target' => 'newest' |
||||
|
], |
||||
|
'php_unit_fqcn_annotation' => true, |
||||
|
'php_unit_internal_class' => [ // phpunit tests should be marked internal |
||||
|
'types' => ['normal', 'final'] |
||||
|
], |
||||
|
'php_unit_mock' => [ // use createMock instead of getMock |
||||
|
'target' => 'newest' |
||||
|
], |
||||
|
'php_unit_mock_short_will_return' => true, |
||||
|
'php_unit_namespaced' => true, |
||||
|
'php_unit_no_expectation_annotation' => [ |
||||
|
'target'=>'newest' |
||||
|
], |
||||
|
'php_unit_ordered_covers' => true, |
||||
|
'php_unit_set_up_tear_down_visibility' => true, |
||||
|
//'php_unit_size_class' => true, // tests should have @small/@medium/@large annotation |
||||
|
'php_unit_strict' => [ |
||||
|
], |
||||
|
'php_unit_test_annotation' => [ // add @test annotation to tests |
||||
|
], |
||||
|
'php_unit_test_case_static_method_calls' => [ |
||||
|
], |
||||
|
'php_unit_test_class_requires_covers' => true, |
||||
|
'phpdoc_add_missing_param_annotation' => [ |
||||
|
'only_untyped' => false |
||||
|
], |
||||
|
'phpdoc_align' => [ |
||||
|
'align' => 'vertical' |
||||
|
], |
||||
|
'phpdoc_annotation_without_dot' => true, |
||||
|
'phpdoc_indent' => true, |
||||
|
'phpdoc_inline_tag' => true, |
||||
|
'phpdoc_no_access' => true, |
||||
|
//'phpdoc_no_alias_tag' => [], |
||||
|
'phpdoc_no_empty_return' => false, |
||||
|
'phpdoc_no_package' => true, |
||||
|
'phpdoc_no_useless_inheritdoc' => true, |
||||
|
'phpdoc_order' => true, |
||||
|
'phpdoc_return_self_reference' => [], |
||||
|
'phpdoc_scalar' => true, |
||||
|
'phpdoc_separation' => false, |
||||
|
'phpdoc_single_line_var_spacing' => true, |
||||
|
'phpdoc_summary' => true, |
||||
|
//'phpdoc_to_comment' => false, // we sometimes use these for phan suppression and stuff |
||||
|
//'phpdoc_to_return_type' => [ 'scalar_types' => true ], |
||||
|
'phpdoc_trim' => true, |
||||
|
'phpdoc_trim_consecutive_blank_line_separation' => true, |
||||
|
'phpdoc_types' => [ // fix casing of types in phpdoc |
||||
|
'groups' => ['simple', 'alias', 'meta'] |
||||
|
], |
||||
|
'phpdoc_types_order' => [ |
||||
|
'null_adjustment' => 'always_last', |
||||
|
'sort_algorithm' => 'none' |
||||
|
], |
||||
|
'phpdoc_var_annotation_correct_order' => true, |
||||
|
'phpdoc_var_without_name' => true, |
||||
|
'pow_to_exponentiation' => true, |
||||
|
//'protected_to_private' => true, // convert protected methods to private ones |
||||
|
'psr4' => true, // class name should match filename |
||||
|
'random_api_migration' => [ // replace rand/srand/etc with the mt_ funcs |
||||
|
], |
||||
|
'return_assignment' => true, |
||||
|
'return_type_declaration' => [ |
||||
|
'space_before' => 'none' |
||||
|
], |
||||
|
//'self_accessor' => false, // force use of self instead of class name |
||||
|
'semicolon_after_instruction' => true, |
||||
|
'set_type_to_cast' => true, // use casting not settype |
||||
|
'short_scalar_cast' => true, // use bool not boolean, int not integer, etc |
||||
|
'simple_to_complex_string_variable' => false, // convert ${var} to {$var} in strings |
||||
|
'simplified_null_return' => true, // don't `return null`, just `return` |
||||
|
'single_blank_line_at_eof' => true, |
||||
|
'single_blank_line_before_namespace' => true, |
||||
|
'single_class_element_per_statement' => [ |
||||
|
'elements' => ['const', 'property'] |
||||
|
], |
||||
|
'single_import_per_statement' => true, |
||||
|
'single_line_after_imports' => true, |
||||
|
'single_line_comment_style' => [ |
||||
|
'comment_types' => ['asterisk', 'hash'] |
||||
|
], |
||||
|
'single_quote' => [ |
||||
|
'strings_containing_single_quote_chars' => false |
||||
|
], |
||||
|
'single_trait_insert_per_statement' => true, |
||||
|
'space_after_semicolon' => [ |
||||
|
'remove_in_empty_for_expressions' => false |
||||
|
], |
||||
|
'standardize_increment' => true, |
||||
|
'standardize_not_equals' => true, |
||||
|
'static_lambda' => true, |
||||
|
//'strict_comparsion' => true, // would be good to *check* but not change |
||||
|
//'strict_param' => true, |
||||
|
//'string_line_ending' => true, // could screw up literals that are used for comparison to outside sourced data |
||||
|
'switch_case_semicolon_to_colon' => true, |
||||
|
'switch_case_space' => true, |
||||
|
'ternary_operator_spaces' => true, |
||||
|
'ternary_to_null_coalescing' => true, |
||||
|
//'trailing_comma_in_multiline_array' => [ // no option to *remove* |
||||
|
//], |
||||
|
'trim_array_spaces' => true, |
||||
|
'unary_operator_spaces' => true, |
||||
|
'visibility_required' => [ |
||||
|
'elements' => ['property', 'method', 'const'] |
||||
|
], |
||||
|
//'void_return' => true, // adds a void return type to functions without a @return/:return |
||||
|
'whitespace_after_comma_in_array' => true, |
||||
|
//'yoda_style' => [ // would be a good habit but I find it hard to read |
||||
|
// 'always_move_variable' => false, |
||||
|
// 'equal' => false, |
||||
|
// 'identical' => false, |
||||
|
// 'less_and_greater' => false |
||||
|
//] |
||||
|
]) |
||||
|
->setIndent("\t") |
||||
|
->setLineEnding("\n") |
||||
|
->setFinder($finder) |
||||
|
; |
File diff suppressed because it is too large
@ -0,0 +1,70 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
'authn' => [ |
||||
|
/* |
||||
|
* Enable/disable the authentication component |
||||
|
* |
||||
|
* When disabled, the authentication service provider, routes, |
||||
|
* and all other components of the authentication system are not |
||||
|
* registered with Laravel. |
||||
|
*/ |
||||
|
'enable' => true, |
||||
|
|
||||
|
// Scopes to request from the OIDC service |
||||
|
'scopes' => ['email'], |
||||
|
|
||||
|
/* |
||||
|
* Path to the OIDC config |
||||
|
* |
||||
|
* This uses the Laravel storage driver, so this needs to be configured to |
||||
|
* use one of the filesystems set up in config/filesystems.php. |
||||
|
*/ |
||||
|
'config' => [ |
||||
|
'disk' => 'local', |
||||
|
'path' => 'auth.json' |
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
* Customize registered routes |
||||
|
* |
||||
|
* All routes are registered with \Route::group(this_array, function() {}) |
||||
|
* At minimum, you should specify 'middleware' and 'prefix' though you |
||||
|
* can specify any options that Laravel's router will accept. |
||||
|
*/ |
||||
|
'routing' => [ |
||||
|
'middleware' => 'web', |
||||
|
'prefix' => '/auth' |
||||
|
], |
||||
|
|
||||
|
/* |
||||
|
* URL to redirect the user to at various points in the process |
||||
|
* |
||||
|
* These are run through Laravel's url() helper, so can be relative (in which |
||||
|
* case they will respect the app's current URL/APP_URL/etc) or absolute. |
||||
|
* |
||||
|
* post_login: After a successful login |
||||
|
* post_logout: After a successful logout. Must be configured in SSO service. |
||||
|
*/ |
||||
|
'urls' => [ |
||||
|
'post_login' => '/', |
||||
|
'post_logout' => '/' |
||||
|
] |
||||
|
], |
||||
|
|
||||
|
'authz' => [ |
||||
|
/* |
||||
|
* Enable/disable the authorization component |
||||
|
* |
||||
|
* Currently authorization is not implemented. |
||||
|
*/ |
||||
|
'enable' => true, |
||||
|
|
||||
|
'provider' => [ |
||||
|
'url' => env('AUTHZ_HOST', 'http://localhost:4466/') |
||||
|
] |
||||
|
] |
||||
|
]; |
@ -0,0 +1,42 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class Authkit2UsersUpdateMinimal extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
// These are split up like this as to not cause issues when running |
||||
|
// against sqlite. |
||||
|
Schema::table('users', static function(Blueprint $table) { |
||||
|
$table->string('password')->nullable()->change(); |
||||
|
}); |
||||
|
Schema::table('users', static function(BluePrint $table) { |
||||
|
$table->string('authkit_id')->unique()->nullable(); |
||||
|
$table->text('authkit_access_token')->nullable(); |
||||
|
$table->text('authkit_refresh_token')->nullable(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::table('users', static function(Blueprint $table) { |
||||
|
$table->dropColumn(['authkit_id', 'authkit_access_token', 'authkit_refresh_token']); |
||||
|
$table->string('password')->nullable(false)->default(null)->change(); |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
use Illuminate\Database\Migrations\Migration; |
||||
|
use Illuminate\Database\Schema\Blueprint; |
||||
|
use Illuminate\Support\Facades\Schema; |
||||
|
|
||||
|
class Authkit2UsersUpdate extends Migration |
||||
|
{ |
||||
|
/** |
||||
|
* Run the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function up() |
||||
|
{ |
||||
|
// These are split up like this as to not cause issues when running |
||||
|
// against sqlite. |
||||
|
Schema::table('users', static function(Blueprint $table) { |
||||
|
$table->dropColumn(['email_verified_at', 'password']); |
||||
|
}); |
||||
|
Schema::table('users', static function(Blueprint $table) { |
||||
|
$table->string('authkit_id')->unique(); |
||||
|
$table->text('authkit_access_token'); |
||||
|
$table->text('authkit_refresh_token'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reverse the migrations. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function down() |
||||
|
{ |
||||
|
Schema::table('users', static function(Blueprint $table) { |
||||
|
$table->dropColumn(['authkit_id', 'authkit_access_token', 'authkit_refresh_token']); |
||||
|
$table->timestamp('email_verified_at')->nullable(); |
||||
|
$table->string('password'); |
||||
|
}); |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
use authkit2\Http\Controllers\AuthenticationController; |
||||
|
|
||||
|
\Route::get('/login', [AuthenticationController::class, 'login'])->name('login'); |
||||
|
\Route::get('/logout', [AuthenticationController::class, 'logout'])->name('logout'); |
||||
|
\Route::get('/callback', [AuthenticationController::class, 'callback'])->name('login.callback'); |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Events; |
||||
|
|
||||
|
use Illuminate\Queue\SerializesModels; |
||||
|
use Illuminate\Foundation\Events\Dispatchable; |
||||
|
|
||||
|
/** |
||||
|
* Event providing a user model as context |
||||
|
*/ |
||||
|
abstract class UserEvent |
||||
|
{ |
||||
|
use Dispatchable; |
||||
|
use SerializesModels; |
||||
|
|
||||
|
/** |
||||
|
* User that this event refers to |
||||
|
* |
||||
|
* @var mixed |
||||
|
*/ |
||||
|
public $user; |
||||
|
|
||||
|
/** |
||||
|
* Initialize new event |
||||
|
* |
||||
|
* @param mixed $user |
||||
|
*/ |
||||
|
public function __construct($user) |
||||
|
{ |
||||
|
$this->user = $user; |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Events; |
||||
|
|
||||
|
/** |
||||
|
* Notification that a user has logged into the app |
||||
|
*/ |
||||
|
class UserLogin extends UserEvent |
||||
|
{ |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Events; |
||||
|
|
||||
|
/** |
||||
|
* Notification that a user has logged into the app |
||||
|
*/ |
||||
|
class UserLogout extends UserEvent |
||||
|
{ |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Events; |
||||
|
|
||||
|
/** |
||||
|
* Notification that a user logging in is brand new |
||||
|
* to this app. |
||||
|
*/ |
||||
|
class UserRegistration extends UserEvent |
||||
|
{ |
||||
|
} |
@ -0,0 +1,133 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Http\Controllers; |
||||
|
use Illuminate\Http\Request; |
||||
|
|
||||
|
/** |
||||
|
* Methods for handling user authentication operations |
||||
|
*/ |
||||
|
class AuthenticationController extends Controller |
||||
|
{ |
||||
|
protected $user_flow; |
||||
|
|
||||
|
public function __construct(\authkit2\Oidc\Flows\UserFlow $user_flow) |
||||
|
{ |
||||
|
$this->user_flow = $user_flow; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Start the login flow for a user |
||||
|
* |
||||
|
* Redirects the user to the SSO service |
||||
|
* |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
public function login() |
||||
|
{ |
||||
|
// TODO: Pass in 'previous' URL from session so we can redirect back |
||||
|
// if we were redirected to login from a guard? |
||||
|
return redirect($this->user_flow->getRedirectUrl(config('authkit.authn.openid.redirect_uri'), config('authkit.authn.scopes'))); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Handle the response from the SSO service |
||||
|
* |
||||
|
* Exchange the code for a token and fetches basic user information. |
||||
|
* Attempts to log the user into this app, and creates them if they |
||||
|
* don't exist. Then redirects the user to the configured post_login url. |
||||
|
* |
||||
|
* @TODO fix type hint on request \/ |
||||
|
* @param $request |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
public function callback(Request $request) |
||||
|
{ |
||||
|
// Get the user class from the Laravel auth config |
||||
|
$user_class = config('auth.providers.users.model'); |
||||
|
|
||||
|
// Verify the passed in state value |
||||
|
$this->user_flow->validateState($request->state); |
||||
|
|
||||
|
// TODO: Check for error response |
||||
|
|
||||
|
// Exchange the code for a token |
||||
|
$token = $this->user_flow->exchangeCodeForToken(config('authkit.authn.openid.redirect_uri'), $request->code); |
||||
|
$user_info = $token->getUserInfo(); |
||||
|
|
||||
|
// Try and use the token to find the local user |
||||
|
$user = \Auth::loginUsingId($token->getUserId()); |
||||
|
|
||||
|
// If that failed |
||||
|
if ($user === false) |
||||
|
{ |
||||
|
// User doesn't exist, create them. |
||||
|
$user = new $user_class(); |
||||
|
$id_field = $user->getAuthIdentifierName(); |
||||
|
$user->{$id_field} = $token->getUserId(); |
||||
|
$user->name = $user_info['name']; |
||||
|
$user->email = $user_info['email']; |
||||
|
if ($user instanceof \authkit2\Models\IAuthkitUser) |
||||
|
{ |
||||
|
$user->{$user->getAccessTokenName()} = $token->getAccessToken(); |
||||
|
$user->{$user->getRefreshTokenName()} = $token->getRefreshToken(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
$user->authkit_access_token = $token->getAccessToken(); |
||||
|
$user->authkit_refresh_token = $token->getRefreshToken(); |
||||
|
} |
||||
|
$register_event_result = event(new \authkit2\Events\UserRegistration($user)); |
||||
|
if (sizeof($register_event_result)) |
||||
|
{ |
||||
|
foreach ($register_event_result as $result) |
||||
|
{ |
||||
|
if ($result instanceof $user_class) |
||||
|
{ |
||||
|
$user = $result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (!$user->exists) |
||||
|
{ |
||||
|
$user->save(); |
||||
|
} |
||||
|
\Auth::login($user); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
$user->name = $user_info['name']; |
||||
|
$user->email = $user_info['email']; |
||||
|
if ($user instanceof \authkit2\Models\IAuthkitUser) |
||||
|
{ |
||||
|
$user->{$user->getAccessTokenName()} = $token->getAccessToken(); |
||||
|
$user->{$user->getRefreshTokenName()} = $token->getRefreshToken(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
$user->authkit_access_token = $token->getAccessToken(); |
||||
|
$user->authkit_refresh_token = $token->getRefreshToken(); |
||||
|
} |
||||
|
$user->save(); |
||||
|
} |
||||
|
|
||||
|
event(new \authkit2\Events\UserLogin($user)); |
||||
|
|
||||
|
return redirect(url(config('authkit.authn.urls.post_login'))); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Explicitly log out of this application and the SSO service |
||||
|
* |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
public function logout() |
||||
|
{ |
||||
|
event(new \authkit2\Events\UserLogout(\Auth::user())); |
||||
|
// Log out locally |
||||
|
\Auth::logout(); |
||||
|
// Redirect to log out remotely as well |
||||
|
return redirect($this->user_flow->getLogoutUrl(url(config('authkit.authn.urls.post_logout')))); |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Http\Controllers; |
||||
|
|
||||
|
use Illuminate\Routing\Controller as BaseController; |
||||
|
|
||||
|
/** |
||||
|
* Base controller class |
||||
|
*/ |
||||
|
class Controller extends BaseController |
||||
|
{ |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace authkit2\Models; |
||||
|
|
||||
|
interface IAuthkitUser |
||||
|
{ |
||||
|
public function getAccessTokenName(): string; |
||||
|
public function getRefreshTokenName(): string; |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Models; |
||||
|
|
||||
|
use Illuminate\Foundation\Auth\User as Authenticatable; |
||||
|
|
||||
|
/** |
||||
|
* Sample user model compatible with authkit |
||||
|
*/ |
||||
|
class User extends Authenticatable |
||||
|
{ |
||||
|
/** @var string[] */ |
||||
|
protected $fillable = [ |
||||
|
'name', |
||||
|
'email' |
||||
|
]; |
||||
|
|
||||
|
/** @var string[] */ |
||||
|
protected $hidden = [ |
||||
|
'remember_token' |
||||
|
]; |
||||
|
|
||||
|
public function getAuthIdentifierName() |
||||
|
{ |
||||
|
return 'authkit_id'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace authkit2\Observers; |
||||
|
|
||||
|
class UserObserver |
||||
|
{ |
||||
|
/*'retrieved', 'creating', 'created', 'updating', 'updated', |
||||
|
'saving', 'saved', 'restoring', 'restored', 'replicating', |
||||
|
'deleting', 'deleted', 'forceDeleted',*/ |
||||
|
|
||||
|
public function retrieved($user) |
||||
|
{ |
||||
|
if ($user instanceof \authkit2\Models\IAuthkitUser) |
||||
|
{ |
||||
|
$token = $user->{$user->getAccessTokenName()}; |
||||
|
$refresh = $user->{$user->getRefreshTokenName()}; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
$token = $user->authkit_access_token; |
||||
|
$refresh = $user->authkit_refresh_token; |
||||
|
} |
||||
|
|
||||
|
$user->authkit = \authkit2\Oidc\Token::fromString($token, $refresh); |
||||
|
// TODO: If access_token is expired, try refresh |
||||
|
// If refresh_token is expired, ?!! |
||||
|
// \Illuminate\Auth\Access\UnauthorizedException |
||||
|
} |
||||
|
|
||||
|
public function saving($user) |
||||
|
{ |
||||
|
unset($user->authkit); |
||||
|
} |
||||
|
} |
@ -0,0 +1,57 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace authkit2\Providers; |
||||
|
|
||||
|
use Illuminate\Support\ServiceProvider; |
||||
|
|
||||
|
class Authkit2ServiceProvider extends ServiceProvider |
||||
|
{ |
||||
|
|
||||
|
/** |
||||
|
* Register all providers and other components for any enabled features |
||||
|
* of the library. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function register(): void |
||||
|
{ |
||||
|
$this->mergeConfigFrom(__DIR__.'/../../config/authkit.php', 'authkit'); |
||||
|
|
||||
|
if (config('authkit.authn.enable')) |
||||
|
{ |
||||
|
$this->app->register(AuthnServiceProvider::class); |
||||
|
} |
||||
|
|
||||
|
if (config('authkit.authz.enable')) |
||||
|
{ |
||||
|
$this->app->register(AuthzServiceProvider::class); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Register publishable resources |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function boot(): void |
||||
|
{ |
||||
|
if ($this->app->runningInConsole()) |
||||
|
{ |
||||
|
$this->publishes([ |
||||
|
__DIR__.'/../../config/authkit.php' => config_path('authkit.php') |
||||
|
], 'config'); |
||||
|
|
||||
|
$this->publishes([ |
||||
|
__DIR__.'/../../database/migrations/new/authkit2_users_update.php' => database_path('migrations/'.date('Y_m_d_His').'_authkit2_users_update.php') |
||||
|
], 'migrations_new'); |
||||
|
$this->publishes([ |
||||
|
__DIR__.'/../../database/migrations/existing/authkit2_users_update_minimal.php' => database_path('migrations/'.date('Y_m_d_His').'_authkit2_users_update_minimal.php') |
||||
|
], 'migrations_existing'); |
||||
|
} |
||||
|
|
||||
|
$this->app->booted(function($app) { |
||||
|
\authkit2\Oidc\Client::setUrl(config('authkit.authn.openid.endpoint')); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,122 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Providers; |
||||
|
|
||||
|
use Illuminate\Support\ServiceProvider; |
||||
|
|
||||
|
/** |
||||
|
* Authentication provider to perform setup for authentication processes |
||||
|
*/ |
||||
|
class AuthnServiceProvider extends ServiceProvider |
||||
|
{ |
||||
|
/** |
||||
|
* Register the additional service providers the authentication process depends on |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function register(): void |
||||
|
{ |
||||
|
$this->app->singleton(\authkit2\Oidc\Flows\ServiceAccountFlow::class, function($app) { |
||||
|
return new \authkit2\Oidc\Flows\ServiceAccountFlow(config('authkit.authn.openid.client_id'), config('authkit.authn.openid.client_secret')); |
||||
|
}); |
||||
|
$this->app->singleton(\authkit2\Oidc\Flows\UserFlow::class, function($app) { |
||||
|
return new \authkit2\Oidc\Flows\UserFlow(config('authkit.authn.openid.client_id'), config('authkit.authn.openid.client_secret')); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Initialize and register all authentication resources |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
public function boot(): void |
||||
|
{ |
||||
|
// register our observer on the user model so we can dynamically add/remove |
||||
|
// the token object + client |
||||
|
$user_model = config('auth.providers.users.model'); |
||||
|
$user_model::observe(\authkit2\Observers\UserObserver::class); |
||||
|
|
||||
|
// Register all authentication routes |
||||
|
$this->bootRoutes(); |
||||
|
// Load keycloak configuration and set in Laravel config() |
||||
|
$this->bootConfig(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Register all authentication routes |
||||
|
* If not already cached, generate and register the URL the SSO service |
||||
|
* should redirect back to. |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
protected function bootRoutes(): void |
||||
|
{ |
||||
|
// Load routes |
||||
|
\Route::group(config('authkit.authn.routing'), function() { |
||||
|
$this->loadRoutesFrom(__DIR__.'/../../routes/web.php'); |
||||
|
}); |
||||
|
|
||||
|
// Figure out where the route is after booting |
||||
|
if (!config()->has('authkit.authn.openid.redirect_uri')) |
||||
|
{ |
||||
|
$this->app->booted(static function() { |
||||
|
// TODO: For some reason route($name) isn't working here. We're |
||||
|
// just looking through the routes manually for now... |
||||
|
|
||||
|
foreach (\Route::getRoutes() as $route) |
||||
|
{ |
||||
|
if ($route->getName() === 'login.callback') |
||||
|
{ |
||||
|
config(['authkit.authn.openid.redirect_uri' => url($route->uri())]); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
throw new \Exception('Route [login.callback] not found'); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Generate any missing config values for keycloak by reading JSON |
||||
|
* auth config |
||||
|
* |
||||
|
* @return void |
||||
|
*/ |
||||
|
protected function bootConfig(): void |
||||
|
{ |
||||
|
// We check if the values are available because they may have |
||||
|
// previously been generated and cached by Laravel in which case |
||||
|
// we can save some work. |
||||
|
if (!(config()->has('authkit.authn.openid.client_id') && |
||||
|
config()->has('authkit.authn.openid.client_secret') && |
||||
|
config()->has('authkit.authn.openid.endpoint'))) |
||||
|
{ |
||||
|
// Figure out where to load the config from |
||||
|
$disk = config('authkit.authn.config.disk'); |
||||
|
$path = config('authkit.authn.config.path'); |
||||
|
if (!\Storage::disk($disk)->exists($path)) |
||||
|
{ |
||||
|
// If it doesn't exist, skip the loading. |
||||
|
$config_json = []; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
$config_raw = \Storage::disk($disk)->get($path); |
||||
|
$config_json = json_decode($config_raw, true); |
||||
|
if (!isset($config_json)) |
||||
|
{ |
||||
|
throw new \Exception("Could not parse authentication configuration at $disk:$path"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
config([ |
||||
|
'authkit.authn.openid.client_id' => env('AUTHKIT_CLIENT_ID', $config_json['resource'] ?? null), |
||||
|
'authkit.authn.openid.client_secret' => env('AUTHKIT_CLIENT_SECRET', $config_json['credentials']['secret'] ?? null), |
||||
|
'authkit.authn.openid.endpoint' => env('AUTHKIT_ENDPOINT', isset($config_json['auth-server-url']) && isset($config_json['realm']) ? $config_json['auth-server-url'].'realms/'.$config_json['realm'] : null) |
||||
|
]); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
<?php |
||||
|
|
||||
|
declare(strict_types=1); |
||||
|
|
||||
|
namespace authkit2\Providers; |
||||
|
|
||||
|
use Illuminate\Support\ServiceProvider; |
||||
|
|
||||
|
/** |
||||
|
* Authorization provider to register and configure all |
||||
|
* assets involved in permission checking |
||||
|
*/ |
||||
|
class AuthzServiceProvider extends ServiceProvider |
||||
|
{ |
||||
|
public function register() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public function boot() |
||||
|
{ |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue