├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── README.md ├── composer.json ├── config └── genealabs-laravel-impersonator.php ├── dist ├── css │ └── tool.css ├── js │ └── tool.js └── mix-manifest.json ├── package.json ├── resources ├── js │ ├── components │ │ └── Tool.vue │ └── tool.js ├── sass │ └── tool.scss └── views │ ├── impersonatees.blade.php │ └── navigation.blade.php ├── routes ├── nova.php └── web.php ├── src ├── Console │ └── Commands │ │ └── Publish.php ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ ├── ImpersonateeController.php │ │ └── Nova │ │ │ └── ImpersonateeController.php │ └── Middleware │ │ └── Authorize.php ├── Impersonator.php ├── NovaImpersonator.php ├── Policies │ └── Impersonation.php ├── Providers │ ├── Service.php │ └── Tool.php └── Traits │ └── Impersonatable.php └── webpack.mix.js /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | ## [7.0.1] 2020-03-01 3 | ### Added 4 | - configuration option for name field. 5 | 6 | ## [7.0.2] 2020-03-06 7 | ### Removed 8 | - pre-Laravel 7 dependencies. 9 | 10 | ## [7.0.1] 2020-03-01 11 | ### Added 12 | - configuration option for name field. 13 | 14 | ## [7.0.0] 2020-02-29 15 | ### Added 16 | - Laravel 7.0 compatibility. 17 | 18 | ## 0.2.3 [5 Oct 2018] 19 | ### Added 20 | - Laravel 5.7 compatibility. 21 | 22 | ## 0.2.2 [1 Jan 2018] 23 | ### Fixed 24 | - view to not use `genealabs/laravel-casts` package. 25 | 26 | ## 0.2.1 [1 Dec 2018] 27 | ### Fixed 28 | - publishing of views and config file. 29 | 30 | ### Added 31 | - more feature tests. 32 | 33 | ## 0.2.0 [31 Dec 2017] 34 | ### Changed 35 | - composer dependency versions to work with Laravel 5.5. 36 | - unit testing process to work without a parent application. 37 | 38 | ## 0.1.2 [9 May 2017] 39 | ### Added 40 | - Caching and restoring or original user's session while they impersonate another user. 41 | 42 | ### Removed 43 | - incorrect composer dependency `illuminate/foundation`. 44 | 45 | ## 0.1.1 [9 May 2017] 46 | ### Added 47 | - composer package dependencies. 48 | - change-log. 49 | 50 | ## 0.1.0 [9 May 2017] 51 | - Initial functionality. 52 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@genealabs.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | We welcome everyone to submit pull requests with: 3 | - fixes for issues 4 | - change suggestions 5 | - updating of documentation 6 | 7 | However, not every pull request will automatically be accepted. I will review each carefully to make sure it is in line with 8 | the direction I want the package to continue in. This might mean that some pull requests are not accepted, or might stay 9 | un-merged until a place for them can be determined. 10 | 11 | ## Testing 12 | - [ ] After making your changes, make sure the tests still pass. 13 | - [ ] When adding new functionality, also add new tests. 14 | - [ ] When fixing errors write and satisfy new unit tests that replicate the issue. 15 | - [ ] Make sure there are no build errors on our CI server (https://ci.genealabs.com/build-status/view/10) 16 | - [ ] All code must past PHPCS and PHPMD PSR2 validation. 17 | 18 | ## Submitting changes 19 | When submitting a pull request, it is important to make sure to complete the following: 20 | - [ ] Add a descriptive header that explains in a single sentence what problem the PR solves. 21 | - [ ] Add a detailed description with animated screen-grab GIFs visualizing how it works. 22 | - [ ] Explain why you think it should be implemented one way vs. another, highlight performance improvements, etc. 23 | 24 | ## Coding conventions 25 | Start reading our code and you'll get the hang of it. We optimize for readability: 26 | - indent using four spaces (soft tabs) 27 | - use Blade for all views 28 | - avoid logic in views, put it in controllers or service classes 29 | - ALWAYS put spaces after list items and method parameters (`[1, 2, 3]`, not `[1,2,3]`), around operators (`x += 1`, not `x+=1`), and around hash arrows. 30 | - this is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible. 31 | - emphasis readability of code over patterns to reduce mental debt 32 | - always add an empty line around structures (if statements, loops, etc.) 33 | 34 | Thanks! 35 | Mike Bronner, GeneaLabs 36 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Issue 2 | *summarize your issue here* 3 | 4 | ## Environment 5 | Laravel Version: *x.y.z* 6 | Laravel Impersonator Package Version: *x.y.z* 7 | PHP Version: *x.y.z* 8 | Homestead Version: *x.y* 9 | Operating System & Version: *name x.y.z* 10 | 11 | ## Stack Trace 12 | ``` 13 | *paste the relevant, complete stack trace here* 14 | ``` 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Laravel Package](https://github.com/GeneaLabs/laravel-impersonator/workflows/Laravel%20Package/badge.svg?branch=master) 2 | [![codecov](https://codecov.io/gh/GeneaLabs/laravel-impersonator/branch/master/graph/badge.svg)](https://codecov.io/gh/GeneaLabs/laravel-impersonator) 3 | 4 | # Impersonator for Laravel 5 | 6 | ![Impersonator for Laravel masthead image.](https://repository-images.githubusercontent.com/90768538/6b44c600-f29c-11e9-88af-65e839679460) 7 | 8 | ## Supporting This Package 9 | This is an MIT-licensed open source project with its ongoing development made possible by the support of the community. If you'd like to support this, and our other packages, please consider [becoming a backer or sponsor on Patreon](https://www.patreon.com/mikebronner). 10 | 11 | ## Pre-requisites 12 | - Laravel 8.0+ 13 | - PHP 7.3+ 14 | 15 | ## Installation 16 | ```sh 17 | composer require genealabs/laravel-impersonator 18 | ``` 19 | 20 | ## Configuration 21 | - `genealabs-laravel-impersonator.layout`: master blade layout view for your application (default `layouts.app`). 22 | - `genealabs-laravel-impersonator.content-section`: name of content section in master layout blade view (default `content`). 23 | - `genealabs-laravel-impersonator.user-model`: user model of your application (default `config('auth.providers.users.model')`). 24 | - `genealabs-laravel-impersonator.middleware`: the middleware to use for the impersonatee routes (default: `['web', 'auth']`). You may specify `only` or `except` parameters. E.g. `['web', 'auth', 'password.confirm' => ['except' => 'destroy']]` 25 | 26 | If you need to customize these settings: 27 | ```sh 28 | php artisan impersonator:publish --config 29 | ``` 30 | 31 | ## Usage 32 | 1. Add trait `GeneaLabs\LaravelImpersonator\Traits\Impersonatable` to your user model. 33 | 2. Override trait method `public function getCanImpersonateAttribute() : bool` that determines if a given user can impersonate other users. 34 | 3. (optional) Override trait method `public function getCanBeImpersonatedAttribute() : bool` that determines if a given user can be impersonated. 35 | 4. Use `route('impersonatees.index')` to view a list of all impersonatable users. 36 | You could add something like the following to your menu: 37 | ```php 38 | @if ((auth()->user()->canImpersonate ?? false) && ! session('impersonator')) 39 | Impersonator 40 | @endif 41 | ``` 42 | 43 | 5. (optional) Add something like the following to your menu view to allow 44 | imporsonator to stop impersonating: 45 | ```php 46 | @if (session('impersonator')) 47 | 51 | End Impersonation Session 52 | 53 | 61 | @else 62 | 65 | Logout 66 | 67 | 75 | @endif 76 | ``` 77 | 78 | ## Customization 79 | ```sh 80 | php artisan impersonator:publish --views 81 | ``` 82 | 83 | ## Credits 84 | In large part prodded and inspired by LaraCasts' tutorial: https://laracasts.com/series/how-do-i/episodes/17. Thank you @JeffreyWay! 85 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "genealabs/laravel-impersonator", 3 | "description": "Impersonate users in your Laravel and Nova apps.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "GeneaLabs, LLC", 8 | "email": "hello@genealabs.com" 9 | } 10 | ], 11 | "repositories": [ 12 | { 13 | "type": "composer", 14 | "url": "https://nova.laravel.com" 15 | } 16 | ], 17 | "require": { 18 | "illuminate/config": "^10.0", 19 | "illuminate/console": "^10.0", 20 | "illuminate/contracts": "^10.0", 21 | "illuminate/http": "^10.0", 22 | "illuminate/routing": "^10.0", 23 | "illuminate/session": "^10.0", 24 | "illuminate/support": "^10.0", 25 | "illuminate/view": "^10.0" 26 | }, 27 | "require-dev": { 28 | "fakerphp/faker": "^1.9", 29 | "mockery/mockery": "^1.4.4", 30 | "nunomaduro/collision": "^7.0", 31 | "phpunit/phpunit": "^10.0", 32 | "laravel/browser-kit-testing": "^7.0", 33 | "php-coveralls/php-coveralls" : "^2.2", 34 | "phpmd/phpmd": "^2.8", 35 | "laravel/nova": "^3.12" 36 | }, 37 | "autoload": { 38 | "classmap": [], 39 | "psr-4": { 40 | "GeneaLabs\\LaravelImpersonator\\": "src/", 41 | "GeneaLabs\\LaravelImpersonator\\Tests\\": "tests/" 42 | } 43 | }, 44 | "extra": { 45 | "laravel": { 46 | "providers": [ 47 | "GeneaLabs\\LaravelImpersonator\\Providers\\Service", 48 | "GeneaLabs\\LaravelImpersonator\\Providers\\Tool" 49 | ] 50 | } 51 | }, 52 | "minimum-stability": "dev", 53 | "prefer-stable": true 54 | } 55 | -------------------------------------------------------------------------------- /config/genealabs-laravel-impersonator.php: -------------------------------------------------------------------------------- 1 | 'layouts.app', 5 | 'content-section' => 'content', 6 | 'user-model' => config('auth.providers.users.model'), 7 | 'orderby-column' => 'name', 8 | 'middleware' => ['web', 'auth'], 9 | ]; 10 | -------------------------------------------------------------------------------- /dist/css/tool.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikebronner/laravel-impersonator/a45ea258e281104b37ee021a21b3987dfafd8457/dist/css/tool.css -------------------------------------------------------------------------------- /dist/js/tool.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){n(1),e.exports=n(11)},function(e,t,n){Nova.booting(function(e,t,r){t.addRoutes([{name:"laravel-impersonator",path:"/laravel-impersonator",component:n(2)}])})},function(e,t,n){var r=n(8)(n(9),n(10),!1,function(e){n(3)},"data-v-18f5d42c",null);e.exports=r.exports},function(e,t,n){var r=n(4);"string"==typeof r&&(r=[[e.i,r,""]]),r.locals&&(e.exports=r.locals);n(6)("70c2a9eb",r,!0,{})},function(e,t,n){(e.exports=n(5)(!1)).push([e.i,"",""])},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=function(e,t){var n=e[1]||"",r=e[3];if(!r)return n;if(t&&"function"==typeof btoa){var o=(s=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(s))))+" */"),a=r.sources.map(function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"});return[n].concat(a).concat([o]).join("\n")}var s;return[n].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},o=0;on.parts.length&&(r.parts.length=n.parts.length)}else{var s=[];for(o=0;o 2 | export default { 3 | data: function () { 4 | return { 5 | isLoading: true, 6 | impersonatees: [], 7 | }; 8 | }, 9 | 10 | mounted() { 11 | this.loadImpersonatees(); 12 | }, 13 | 14 | methods: { 15 | impersonate: function (userId) { 16 | var self = this; 17 | 18 | Nova.request().put("/genealabs/laravel-impersonator/nova/impersonatees/" + userId) 19 | .then(function (response) { 20 | window.location.href = "/dashboard"; 21 | }) 22 | .catch(function (error) { 23 | console.error(error.data); 24 | }); 25 | }, 26 | 27 | loadImpersonatees: function () { 28 | var self = this; 29 | 30 | Nova.request().get("/genealabs/laravel-impersonator/nova/impersonatees") 31 | .then(function (response) { 32 | console.log(response.data); 33 | self.isLoading = false; 34 | self.impersonatees = response.data; 35 | }) 36 | .catch(function (error) { 37 | console.error(error.data); 38 | }); 39 | }, 40 | }, 41 | }; 42 | 43 | 44 | 90 | 91 | 94 | -------------------------------------------------------------------------------- /resources/js/tool.js: -------------------------------------------------------------------------------- 1 | Nova.booting((Vue, router, store) => { 2 | router.addRoutes([ 3 | { 4 | name: 'laravel-impersonator', 5 | path: '/laravel-impersonator', 6 | component: require('./components/Tool'), 7 | }, 8 | ]) 9 | }) 10 | -------------------------------------------------------------------------------- /resources/sass/tool.scss: -------------------------------------------------------------------------------- 1 | // Nova Tool CSS 2 | -------------------------------------------------------------------------------- /resources/views/impersonatees.blade.php: -------------------------------------------------------------------------------- 1 | @extends(config('genealabs-laravel-impersonator.layout')) 2 | 3 | @section(config('genealabs-laravel-impersonator.content-section')) 4 |
5 | 6 | @if((auth()->user()->canImpersonate ?? false) && ! session('impersonator')) 7 |
8 | 9 | @foreach($users as $user) 10 |
14 | {{ csrf_field () }} 15 | {{ method_field ('PUT') }} 16 | 17 | 20 | {{ $user->name }} 21 |
22 | @endforeach 23 | 24 |
25 | @else 26 |
27 |

28 | 29 | You are already impersonating someone. Please end this 30 | impersonation session before trying to impersonate someone else. 31 |

32 |
33 | @endif 34 | 35 |
36 | @endsection 37 | -------------------------------------------------------------------------------- /resources/views/navigation.blade.php: -------------------------------------------------------------------------------- 1 | @if (session("impersonator")) 2 |
6 | {{ method_field('DELETE') }} 7 | {{ csrf_field() }} 8 |

12 | 13 | 14 | Stop Impersonating 15 | 16 | 17 |

18 |
19 | @elseif (auth()->user()->hasRole("Admin") || auth()->user()->hasRole("SuperAdmin")) 20 | 21 | 22 | 23 | Impersonate Users 24 | 25 | 26 | @endif 27 | -------------------------------------------------------------------------------- /routes/nova.php: -------------------------------------------------------------------------------- 1 | resource('impersonatees', "ImpersonateeController"); 17 | 18 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | resource('impersonatees', ImpersonateeController::class); 6 | -------------------------------------------------------------------------------- /src/Console/Commands/Publish.php: -------------------------------------------------------------------------------- 1 | options()) === 0) { 14 | $this->info('Please use at least one of the options: "impersonator:publish {--config} {--views}"'); 15 | } 16 | 17 | if ($this->option('views')) { 18 | $this->delTree(resource_path('views/vendor/genealabs-laravel-impersonator')); 19 | $this->call('vendor:publish', [ 20 | '--provider' => Service::class, 21 | '--tag' => ['views'], 22 | '--force' => true, 23 | ]); 24 | } 25 | 26 | if ($this->option('config')) { 27 | $this->call('vendor:publish', [ 28 | '--provider' => Service::class, 29 | '--tag' => ['config'], 30 | '--force' => true, 31 | ]); 32 | } 33 | } 34 | 35 | private function delTree($folder) 36 | { 37 | if (! is_dir($folder)) { 38 | return false; 39 | } 40 | 41 | $files = array_diff(scandir($folder), ['.','..']); 42 | 43 | foreach ($files as $file) { 44 | is_dir("$folder/$file") ? $this->delTree("$folder/$file") : unlink("$folder/$file"); 45 | } 46 | 47 | return rmdir($folder); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | userClass = config('genealabs-laravel-impersonator.user-model'); 15 | 16 | // Allow us to customise the middleware being used for each route. 17 | foreach (config('genealabs-laravel-impersonator.middleware', ['web', 'auth']) as $middleware => $config) { 18 | if (is_int($middleware)) { 19 | $middleware = $config; 20 | } 21 | 22 | $middleware = $this->middleware($middleware); 23 | 24 | if ($config['only'] ?? false) { 25 | $middleware->only($config['only']); 26 | } 27 | if ($config['except'] ?? false) { 28 | $middleware->except($config['except']); 29 | } 30 | } 31 | } 32 | 33 | public function index() : View 34 | { 35 | $this->authorize('impersonation', new Impersonator); 36 | 37 | $users = (new $this->userClass)->orderBy(config('genealabs-laravel-impersonator.orderby-column')) 38 | ->get() 39 | ->filter(function ($user) { 40 | return $user->canBeImpersonated; 41 | }); 42 | 43 | return view('genealabs-laravel-impersonator::impersonatees')->with([ 44 | 'users' => $users, 45 | ]); 46 | } 47 | 48 | public function update($impersonatee) : RedirectResponse 49 | { 50 | $this->authorize('impersonation', new Impersonator); 51 | 52 | $impersonator = auth()->user(); 53 | $oldSession = session()->all(); 54 | session()->flush(); 55 | auth()->login($impersonatee); 56 | session([ 57 | 'impersonator' => $impersonator, 58 | 'impersonator-session-data' => $oldSession, 59 | ]); 60 | 61 | return redirect('/'); 62 | } 63 | 64 | public function destroy() : Response 65 | { 66 | $impersonator = session('impersonator'); 67 | $this->authorizeForUser($impersonator, 'impersonation', new Impersonator); 68 | $originalSession = session('impersonator-session-data'); 69 | session()->flush(); 70 | session($originalSession); 71 | auth()->login($impersonator); 72 | 73 | if (request()->ajax()) { 74 | return response(null, 204); 75 | } 76 | 77 | return redirect('/'); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Http/Controllers/Nova/ImpersonateeController.php: -------------------------------------------------------------------------------- 1 | userClass = config('genealabs-laravel-impersonator.user-model'); 17 | } 18 | 19 | public function index() : Collection 20 | { 21 | // TODO: figure out why these are failing authorization 22 | // $this->authorize('impersonation', new Impersonator); 23 | 24 | return (new $this->userClass)->orderBy('name') 25 | ->get() 26 | ->filter(function ($user) { 27 | return $user->canBeImpersonated; 28 | }); 29 | } 30 | 31 | public function update($impersonatee) : Response 32 | { 33 | // TODO: figure out why these are failing authorization 34 | // $this->authorize('impersonation', new Impersonator); 35 | $impersonator = auth()->user(); 36 | $oldSession = session()->all(); 37 | session()->flush(); 38 | auth()->login($impersonatee); 39 | session([ 40 | 'impersonator' => $impersonator, 41 | 'impersonator-session-data' => $oldSession, 42 | ]); 43 | 44 | return response(null, 204); 45 | } 46 | 47 | public function destroy() : Response 48 | { 49 | $impersonator = session('impersonator'); 50 | $this->authorizeForUser($impersonator, 'impersonation', new Impersonator); 51 | $originalSession = session('impersonator-session-data'); 52 | session()->flush(); 53 | session($originalSession); 54 | auth()->login($impersonator); 55 | 56 | return redirect(config("nova.path")); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Http/Middleware/Authorize.php: -------------------------------------------------------------------------------- 1 | first([$this, 'matchesTool']); 19 | 20 | return optional($tool)->authorize($request) 21 | ? $next($request) 22 | : abort(403); 23 | } 24 | 25 | /** 26 | * Determine whether this tool belongs to the package. 27 | * 28 | * @param \Laravel\Nova\Tool $tool 29 | * @return bool 30 | */ 31 | public function matchesTool($tool) 32 | { 33 | return $tool instanceof NovaImpersonator; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Impersonator.php: -------------------------------------------------------------------------------- 1 | canImpersonate 8 | ?? false; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Providers/Service.php: -------------------------------------------------------------------------------- 1 | Impersonation::class, 13 | ]; 14 | 15 | public function boot() 16 | { 17 | $configPath = __DIR__ . '/../../config/genealabs-laravel-impersonator.php'; 18 | $routesPath = __DIR__ . '/../../routes/web.php'; 19 | $viewsFolder = __DIR__ . '/../../resources/views'; 20 | 21 | $this->publishes([ 22 | $configPath => config_path('genealabs-laravel-impersonator.php') 23 | ], 'config'); 24 | $this->publishes([ 25 | $viewsFolder => resource_path('views/vendor/genealabs-laravel-impersonator'), 26 | ], 'views'); 27 | 28 | $this->mergeConfigFrom($configPath, 'genealabs-laravel-impersonator'); 29 | $this->loadViewsFrom($viewsFolder, 'genealabs-laravel-impersonator'); 30 | $this->loadRoutesFrom($routesPath); 31 | 32 | app('router')->model( 33 | 'impersonatee', 34 | config('genealabs-laravel-impersonator.user-model') 35 | ); 36 | 37 | $this->registerPolicies(); 38 | $this->commands(Publish::class); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Providers/Tool.php: -------------------------------------------------------------------------------- 1 | loadViewsFrom(__DIR__ . '/../../resources/views', 'laravel-impersonator'); 13 | 14 | $this->app->booted(function () { 15 | $this->routes(); 16 | }); 17 | 18 | \Laravel\Nova\Nova::serving(function (\Laravel\Nova\Events\ServingNova $event) { 19 | // 20 | }); 21 | } 22 | } 23 | 24 | /** 25 | * Register the tool's routes. 26 | * 27 | * @return void 28 | */ 29 | protected function routes() 30 | { 31 | if ($this->app->routesAreCached()) { 32 | return; 33 | } 34 | 35 | Route::middleware(['nova', Authorize::class]) 36 | ->prefix('genealabs/laravel-impersonator/nova') 37 | ->namespace("GeneaLabs\LaravelImpersonator\Http\Controllers\Nova") 38 | ->group(__DIR__ . '/../../routes/nova.php'); 39 | } 40 | 41 | /** 42 | * Register any application services. 43 | * 44 | * @return void 45 | */ 46 | public function register() 47 | { 48 | // 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Traits/Impersonatable.php: -------------------------------------------------------------------------------- 1 | attributes["can_impersonate"] 8 | ?? false; 9 | } 10 | 11 | public function setCanImpersonateAttribute(bool $value) 12 | { 13 | $this->attributes["can_impersonate"] = $value; 14 | } 15 | 16 | public function getCanBeImpersonatedAttribute() : bool 17 | { 18 | return $this->attributes["can_be_impersonated"] 19 | ?? true; 20 | } 21 | 22 | public function setCanBeImpersonatedAttribute(bool $value) 23 | { 24 | $this->attributes["can_be_impersonated"] = $value; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | let mix = require('laravel-mix') 2 | 3 | mix.setPublicPath('dist') 4 | .js('resources/js/tool.js', 'js') 5 | .sass('resources/sass/tool.scss', 'css') 6 | --------------------------------------------------------------------------------