From 22072b621701c67aad4195b02d3a0cca3a368648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Kottal?= Date: Thu, 30 Jun 2022 22:45:23 +0200 Subject: [PATCH] First commit --- .gitattributes | 17 + .github/FUNDING.yml | 1 + .github/workflows/ci.yml | 19 ++ .github/workflows/release-preview.yml | 22 ++ .github/workflows/release.yml | 28 ++ .gitignore | 303 ++++++++++++++++++ CHANGELOG.md | 15 + CONTRIBUTING.md | 138 ++++++++ LICENSE.md | 21 ++ README-nuget.md | 36 +++ README.md | 57 ++++ docs/impersonate-user.png | Bin 0 -> 11447 bytes docs/impersonating-user.png | Bin 0 -> 9231 bytes docs/impersonator-icon.png | Bin 0 -> 6462 bytes .../ImpersonatedUserId.cs | 17 + .../ImpersonatorComposer.cs | 13 + .../ImpersonatorManifestFilter.cs | 34 ++ .../Our.Umbraco.Impersonator.csproj | 30 ++ .../UserController.cs | 129 ++++++++ .../wwwroot/lang/da-DK.xml | 20 ++ .../wwwroot/lang/en-US.xml | 20 ++ .../wwwroot/user.controller.js | 86 +++++ .../wwwroot/user.html | 40 +++ 23 files changed, 1046 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release-preview.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.md create mode 100644 README-nuget.md create mode 100644 README.md create mode 100644 docs/impersonate-user.png create mode 100644 docs/impersonating-user.png create mode 100644 docs/impersonator-icon.png create mode 100644 src/Our.Umbraco.Impersonator/ImpersonatedUserId.cs create mode 100644 src/Our.Umbraco.Impersonator/ImpersonatorComposer.cs create mode 100644 src/Our.Umbraco.Impersonator/ImpersonatorManifestFilter.cs create mode 100644 src/Our.Umbraco.Impersonator/Our.Umbraco.Impersonator.csproj create mode 100644 src/Our.Umbraco.Impersonator/UserController.cs create mode 100644 src/Our.Umbraco.Impersonator/wwwroot/lang/da-DK.xml create mode 100644 src/Our.Umbraco.Impersonator/wwwroot/lang/en-US.xml create mode 100644 src/Our.Umbraco.Impersonator/wwwroot/user.controller.js create mode 100644 src/Our.Umbraco.Impersonator/wwwroot/user.html diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bdb0cab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..1e67c8a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [skttl] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1855e30 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,19 @@ +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/Our.Umbraco.Impersonator + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Build + run: dotnet build --configuration Release diff --git a/.github/workflows/release-preview.yml b/.github/workflows/release-preview.yml new file mode 100644 index 0000000..7afe51d --- /dev/null +++ b/.github/workflows/release-preview.yml @@ -0,0 +1,22 @@ +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+-[a-z]*[0-9][0-9][0-9]" +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/Our.Umbraco.Impersonator + timeout-minutes: 15 + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set VERSION variable from tag + run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV + - name: Pack + run: dotnet pack --configuration Release /p:Version=${VERSION} --output . + - name: Push + run: dotnet nuget push Our.Umbraco.Impersonator.${VERSION}.nupkg --api-key ${NUGET_API_KEY} --source https://api.nuget.org/v3/index.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c2809ce --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/Our.Umbraco.Impersonator + timeout-minutes: 15 + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Verify commit exists in origin/main + run: | + git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* + git branch --remote --contains | grep origin/main + - name: Set VERSION variable from tag + run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV + - name: Build + run: dotnet build --configuration Release /p:Version=${VERSION} + - name: Pack + run: dotnet pack --configuration Release /p:Version=${VERSION} --no-build --output . + - name: Push + run: dotnet nuget push Our.Umbraco.Impersonator.${VERSION}.nupkg --api-key ${NUGET_API_KEY} --source https://api.nuget.org/v3/index.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82c2192 --- /dev/null +++ b/.gitignore @@ -0,0 +1,303 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + + + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..83b1f91 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 2.0.0 + +- Upgraded for Umbraco 10 +- Changed interception hacks for putting in custom markup, to a user dialog dashboard. + +## 1.0.0 + +- First release for Umbraco 7 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0b2ddf8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,138 @@ +# Contributing to this project + +Please take a moment to review this document in order to make the contribution +process easy and effective for everyone involved. + +Following these guidelines helps to communicate that you respect the time of +the developers managing and developing this open source project. In return, +they should reciprocate that respect in addressing your issue or assessing +patches and features. + + +## Using the issue tracker + +The issue tracker is the preferred channel for [bug reports](#bugs), +[features requests](#features) and [submitting pull +requests](#pull-requests), but please respect the following restrictions: + +* Please **do not** derail or troll issues. Keep the discussion on topic and + respect the opinions of others. + + + +## Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. +Good bug reports are extremely helpful - thank you! + +Guidelines for bug reports: + +1. **Use the GitHub issue search** — check if the issue has already been + reported. + +2. **Check if the issue has been fixed** — try to reproduce it using the + latest `master` or development branch in the repository. + +3. **Isolate the problem** — create a reduced test case and a live example. + +A good bug report shouldn't leave others needing to chase you up for more +information. Please try to be as detailed as possible in your report. What is +your environment? What steps will reproduce the issue? What browser(s) and OS +experience the problem? What would you expect to be the outcome? All these +details will help people to fix any potential bugs. + +Example: + +> Short and descriptive example bug report title +> +> A summary of the issue and the browser/OS environment in which it occurs. If +> suitable, include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to the reduced test case +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + + + +## Feature requests + +Feature requests are welcome. But take a moment to find out whether your idea +fits with the scope and aims of the project. It's up to *you* to make a strong +case to convince the project's developers of the merits of this feature. Please +provide as much detail and context as possible. + + + +## Pull requests + +Good pull requests - patches, improvements, new features - are a fantastic +help. They should remain focused in scope and avoid containing unrelated +commits. + +**Please ask first** before embarking on any significant pull request (e.g. +implementing features, refactoring code, porting to a different language), +otherwise you risk spending a lot of time working on something that the +project's developers might not want to merge into the project. + +Please adhere to the coding conventions used throughout a project (indentation, +accurate comments, etc.) and any other requirements (such as test coverage). + +Follow this process if you'd like your work considered for inclusion in the +project: + +1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com// + # Navigate to the newly cloned directory + cd + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com// + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout develop + git pull upstream develop + ``` + +3. Create a new topic branch (off the main project `develop` branch) to + contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your code is unlikely be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +5. Locally merge (or rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream develop + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description. + +**IMPORTANT**: By submitting a patch, you agree to allow the project owner to +license your work under the same license as that used by the project. \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..b534bbd --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Søren Kottal + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README-nuget.md b/README-nuget.md new file mode 100644 index 0000000..f44341f --- /dev/null +++ b/README-nuget.md @@ -0,0 +1,36 @@ +Impersonator for Umbraco 10+ lets administrators impersonate other users. + +This is useful in cases where you need to check the permissions of editors and other users, if you don't know, or don't want to know their passwords. + +When Impersonator is installed, and you are logged in as a user with access to the Users section in Umbraco, you can click your avatar in the upper right corner to impersonate another user. + +When impersonating another user, the user dialog is used to end impersonation. + +## Contributing to this project + +Anyone and everyone is welcome to contribute. Please take a moment to review the [guidelines for contributing](CONTRIBUTING.md). + +- [Bug reports](CONTRIBUTING.md#bugs) +- [Feature requests](CONTRIBUTING.md#features) +- [Pull requests](CONTRIBUTING.md#pull-requests) + +## Contact + +Have a question? + +- [Raise an issue](https://github.com/skttl/Our.Umbraco.Impersonator/issues) on GitHub + +## Dev Team + +- [Søren Kottal](https://github.com/skttl) + +### Special thanks + +- Thanks to [Casey Neehouse](https://github.com/neehouse) for the original idea and code example. +- Thanks to Luis Prado for the icon [Impostor from NounProject.com](https://thenounproject.com/icon/impostor-4583/). + +## License + +Copyright © 2022 Søren Kottal + +Licensed under the [MIT License](LICENSE.md) diff --git a/README.md b/README.md new file mode 100644 index 0000000..6326a2b --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Impersonator + +[![NuGet release](https://img.shields.io/nuget/v/Our.Umbraco.Impersonator.svg)](https://www.nuget.org/packages/Our.Umbraco.Impersonator) +[![Our Umbraco project page](https://img.shields.io/badge/our-umbraco-orange.svg)](https://our.umbraco.com/packages/website-utilities/impersonator/) + +Impersonator for Umbraco 10+ lets administrators impersonate other users. + +This is useful in cases where you need to check the permissions of editors and other users, if you don't know, or don't want to know their passwords. + +When Impersonator is installed, and you are logged in as a user with access to the Users section in Umbraco, you can click your avatar in the upper right corner to impersonate another user. + +![Impersonate a user from the user dialog](docs/impersonate-user.png) + +When impersonating another user, the user dialog is used to end impersonation. + +![Impersonating a user from the user dialog](docs/impersonatinguser.png) + +## Getting started + +### Installation + +Impersonator can be installed in Umbraco 10 using the dotnet cli by running the following command: + +``` +dotnet add package Our.Umbraco.Impersonator +``` + +--- + +## Contributing to this project + +Anyone and everyone is welcome to contribute. Please take a moment to review the [guidelines for contributing](CONTRIBUTING.md). + +- [Bug reports](CONTRIBUTING.md#bugs) +- [Feature requests](CONTRIBUTING.md#features) +- [Pull requests](CONTRIBUTING.md#pull-requests) + +## Contact + +Have a question? + +- [Raise an issue](https://github.com/skttl/Our.Umbraco.Impersonator/issues) on GitHub + +## Dev Team + +- [Søren Kottal](https://github.com/skttl) + +### Special thanks + +- Thanks to [Casey Neehouse](https://github.com/neehouse) for the original idea and code example. +- Thanks to Luis Prado for the icon [Impostor from NounProject.com](https://thenounproject.com/icon/impostor-4583/). + +## License + +Copyright © 2022 Søren Kottal + +Licensed under the [MIT License](LICENSE.md) diff --git a/docs/impersonate-user.png b/docs/impersonate-user.png new file mode 100644 index 0000000000000000000000000000000000000000..648cb05051e2466529b90cf9719e490407d0e95e GIT binary patch literal 11447 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 literal 0 HcmV?d00001 diff --git a/docs/impersonating-user.png b/docs/impersonating-user.png new file mode 100644 index 0000000000000000000000000000000000000000..77b0686003aab56c7218282f01327a72e918f4dd GIT binary patch literal 9231 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 QXKrY8v)UCq)2RbGxhyVZp literal 0 HcmV?d00001 diff --git a/docs/impersonator-icon.png b/docs/impersonator-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..be50f60ce6a5b3fbfc24d2bbd86045ecb4266bc8 GIT binary patch literal 6462 zcmb_gc{o(>+b3Ho)26bG7F3q3p+>gsg|chLnqustLy$;0H}`9cSp9_v^VzPZjpfRPga_aK%qdOf>uFI`n@e*` z3tO+Fsel#5S!cmgdV2Kpue`+7y!&%mKC`qn#`3o<%Iu%d?VF|PKeFOhBHfmL)GT}} zpRbLaD|&m{eBgI| z$r__&p&mCEaBXIC=(nHVjGf{Pd&OxlW)43XXlt5382{c9&dexF7=clEqHxbH{CCY^ zV_M^{XXj4UVRMUr-J(=49r_#RXQ(Ipzm8&I;YhK2F&2}3>DkH9`+O;kPbM%`&kXqS5scB!PUK(bnj6EXM$H@E2%Pg3LBgpIGq=j0 zyH0(IKX+Am0x2AJ4pKv91-;Ipy?OHXcGuz>9{vJMNM!&W1Yo(T3d%BhZeC|aZ)(!15L-=CJY^rSc#Fj!Xn1gXtA%wjckw^jH0pd z4$@7eP%R#rwkIy| zz}b<3tT4Tqwkp6=P9L`(f;&6`<+0`{cJ??%oYjFzpPg0YD_;`2`boqVdzFEoOgKR5 zkm?jEoN(-D0G6A*HLftWvqB44+p^#BdIAA$riqYPZ`0}M;nf#!03L+k^yhE2*9RclKXn4Xf)1A+sgjnn zVhh-8wJd^YRjj?nQDrtDY|HkDr_K5;q_{XQ=mADndVuhrn(o%*PjMAG=oIUCT)vCr z1xGk=ltrEMv%){_8T=?dy3nF&b*VScs;~Uyf`@GykB|J@hf{fIcpMv+aMV%89Y$fp z;%>~yc*5hZVf%_VZI`Ng8>DP(o*g(+Q!2wFyDDMYIf<%hYgzTpeNF5!@tL zse$WFof5rO3M?xUu766<*?{eJhg2S0 ze#X-)5=3d?u+}SX>L1Yc0K@g22VGeZQ%qyPJl4cYIKdzesv?JttuIP8>KMk`K>OPZ#wSA0!E1bl_Aa0Yz>wy)R6h&fu^w0_;LtXKjK6#(3RqC%!j0zM3-b zA9BxND}7n2hi@?V&S-xiR7lzHw}2km

Ti)-SMzlR|kedExzdK0qF}b8VbkE-S{C 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 + } + } +} diff --git a/src/Our.Umbraco.Impersonator/ImpersonatorManifestFilter.cs b/src/Our.Umbraco.Impersonator/ImpersonatorManifestFilter.cs new file mode 100644 index 0000000..8a388fb --- /dev/null +++ b/src/Our.Umbraco.Impersonator/ImpersonatorManifestFilter.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Umbraco.Cms.Core.Manifest; + +namespace Our.Umbraco.Impersonator +{ + public class ImpersonatorManifestFilter : IManifestFilter + { + public void Filter(List manifests) + { + manifests.Add(new PackageManifest() + { + PackageName = "Our.Umbraco.Impersonator", + AllowPackageTelemetry = true, + Dashboards = new[] + { + new ManifestDashboard() + { + Alias = "impersonator", + Sections = new[] + { + "user-dialog" + }, + View = "/App_Plugins/Our.Umbraco.Impersonator/user.html", + Weight = 0 + } + }, + Scripts = new[] + { + "/App_Plugins/Our.Umbraco.Impersonator/user.controller.js" + } + }); + } + } +} diff --git a/src/Our.Umbraco.Impersonator/Our.Umbraco.Impersonator.csproj b/src/Our.Umbraco.Impersonator/Our.Umbraco.Impersonator.csproj new file mode 100644 index 0000000..754741f --- /dev/null +++ b/src/Our.Umbraco.Impersonator/Our.Umbraco.Impersonator.csproj @@ -0,0 +1,30 @@ + + + App_Plugins/Our.Umbraco.Impersonator + net6.0 + . + Impersonator for Umbraco + Our.Umbraco.Impersonator + Impersonator for Umbraco + Søren Kottal + Impersonator for Umbraco 10+ lets administrators impersonate other users + umbraco umbraco-marketplace + impersonator-icon.png + README-nuget.md + LICENSE.md + https://github.com/skttl/umbraco-impersonator + https://github.com/skttl/umbraco-impersonator/blob/main/CHANGELOG.md + Copyright (c) 2022 Søren Kottal + + + + + + + + + + + + + diff --git a/src/Our.Umbraco.Impersonator/UserController.cs b/src/Our.Umbraco.Impersonator/UserController.cs new file mode 100644 index 0000000..ecfb809 --- /dev/null +++ b/src/Our.Umbraco.Impersonator/UserController.cs @@ -0,0 +1,129 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Mapping; +using Umbraco.Cms.Core.Models.ContentEditing; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Web.BackOffice.Controllers; +using Umbraco.Cms.Web.BackOffice.Security; +using Umbraco.Cms.Web.Common.Attributes; + +namespace Our.Umbraco.Impersonator +{ + [PluginController("Impersonator")] + public class UserController : UmbracoAuthorizedApiController + { + private const string IMPERSONATOR_USER_ID = "Impersonator.User.Id"; + + private readonly IUserService _userService; + private readonly IUmbracoMapper _umbracoMapper; + private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor; + private readonly IBackOfficeSignInManager _signInManager; + + public UserController( + IUserService userService, + IUmbracoMapper umbracoMapper, + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IBackOfficeSignInManager signInManager + ) + { + _userService = userService; + _umbracoMapper = umbracoMapper; + _backofficeSecurityAccessor = backOfficeSecurityAccessor; + _signInManager = signInManager; + } + + + private ImpersonatedUserId GetImpersonatingUserId() + { + var impersonatedUserIdString = HttpContext.Session.GetString(IMPERSONATOR_USER_ID); + if (impersonatedUserIdString == null) return null; + + var impersonatedUserId = JsonSerializer.Deserialize(impersonatedUserIdString); + + if (impersonatedUserId == null) return null; + + if (impersonatedUserId.SessionId != HttpContext.Session.Id) + { + HttpContext.Session.Remove(IMPERSONATOR_USER_ID); + return null; + } + + return impersonatedUserId; + } + + [HttpGet] + public string GetImpersonatingUserHash() + { + ImpersonatedUserId impersonatingUserId = GetImpersonatingUserId(); + if (impersonatingUserId == null) + { + return null; + } + var userById = _userService.GetUserById(impersonatingUserId.UserId); + if (userById != null) + { + return _umbracoMapper.Map(userById)?.EmailHash; + } + return null; + } + + [HttpPost] + public async Task EndImpersonation() + { + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser == null) + { + return "notSignedIn"; + } + ImpersonatedUserId impersonatingUserId = GetImpersonatingUserId(); + if (impersonatingUserId == null) + { + return "success"; + } + var userById = _userService.GetUserById(impersonatingUserId.ImpersonatingUserId); + if (userById != null) + { + var user = _umbracoMapper.Map(userById); + HttpContext.Session.Remove(IMPERSONATOR_USER_ID); + await _signInManager.SignOutAsync(); + await _signInManager.SignInAsync(user, false); + + return "success"; + } + return "userNotFound"; + } + + [HttpPost] + public string Impersonate(int id) + { + var currentUser = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + if (currentUser == null) + { + return "notSignedIn"; + } + if (currentUser.AllowedSections.Contains(Constants.Applications.Users)) + { + if (id > 0) + { + var userById = _userService.GetUserById(id); + if (userById != null) + { + var user = _umbracoMapper.Map(userById); + HttpContext.Session.Remove(IMPERSONATOR_USER_ID); + _signInManager.SignOutAsync(); + _signInManager.SignInAsync(user, false); + HttpContext.Session.SetString(IMPERSONATOR_USER_ID, JsonSerializer.Serialize(new ImpersonatedUserId(id, currentUser.Id, HttpContext.Session.Id))); + return "success"; + } + return "userNotFound"; + } + return "invalidUserId"; + } + return "notAdministrator"; + } + } +} diff --git a/src/Our.Umbraco.Impersonator/wwwroot/lang/da-DK.xml b/src/Our.Umbraco.Impersonator/wwwroot/lang/da-DK.xml new file mode 100644 index 0000000..c75d43d --- /dev/null +++ b/src/Our.Umbraco.Impersonator/wwwroot/lang/da-DK.xml @@ -0,0 +1,20 @@ + + + + Søren Kottal + http://skttl.dk + + + Brugerimitering + + + Imitér bruger + Du imiterer i øjeblikket denne bruger. Du kan vende tilbage til din egen bruger, ved at afslutte imiteringen. + Afslut imitering + Du imiterer denne bruger. Genopfrisk Umbraco for at opdatere rettigheder. + Du skal være logget ind i Umbraco for at kunne imitere en anden bruger + Den valgte bruger kunne ikke findes + Det valgte bruger ID er ikke gyldigt + Du skal have administratorrettigheder for at kunne imitere en anden bruger + + \ No newline at end of file diff --git a/src/Our.Umbraco.Impersonator/wwwroot/lang/en-US.xml b/src/Our.Umbraco.Impersonator/wwwroot/lang/en-US.xml new file mode 100644 index 0000000..070e241 --- /dev/null +++ b/src/Our.Umbraco.Impersonator/wwwroot/lang/en-US.xml @@ -0,0 +1,20 @@ + + + + Søren Kottal + http://skttl.dk + + + User Impersonation + + + Impersonate user + You are currently impersonating this user. You can return to your own user, by ending impersonation. + End impersonation + You have impersonated the selected user. Refresh Umbraco to update your permissions. + You need to be signed in to Umbraco, to be able to impersonate another user. + The selected user could not be found. + The selected user id is invalid. + You need administrator rights, to be able to impersonate another user. + + \ No newline at end of file diff --git a/src/Our.Umbraco.Impersonator/wwwroot/user.controller.js b/src/Our.Umbraco.Impersonator/wwwroot/user.controller.js new file mode 100644 index 0000000..6e2989e --- /dev/null +++ b/src/Our.Umbraco.Impersonator/wwwroot/user.controller.js @@ -0,0 +1,86 @@ +(function () { + "use strict"; + + function ImpersonatorUserController( + $scope, + $http, + authResource, + usersResource, + notificationsService, + localizationService + ) { + var vm = this; + + vm.isImpersonating = false; + vm.isLoading = false; + vm.users = []; + vm.loadingUsers = true; + vm.impersonateUserButtonState = "init"; + vm.canImpersonateUsers = false; + + authResource.getCurrentUser().then(function (user) { + if (user.allowedSections.indexOf("users") > -1) { + vm.canImpersonateUsers = true; + + // Get users + usersResource + .getPagedResults({ + pageSize: Number.MAX_SAFE_INTEGER, + userStates: ["Active"], + }) + .then( + function (data) { + vm.users = data.items; + vm.loadingUsers = false; + }, + function (error) { + vm.loadingUsers = false; + } + ); + } + }); + + vm.impersonateUser = function () { + vm.impersonateUserButtonState = "busy"; + + $http + .post( + "/umbraco/backoffice/Impersonator/User/Impersonate/" + + vm.userToImpersonate.id + ) + .then(function success(response) { + if (response.data == "success") { + window.location.reload(); + } else { + vm.impersonateButtonState = "error"; + localizationService + .localize("impersonator_" + response.data) + .then(function (value) { + notificationsService.error(value); + }); + } + }); + }; + + $http + .get("/umbraco/backoffice/Impersonator/User/GetImpersonatingUserHash") + .then(function success(response) { + if (response.data) { + vm.isImpersonating = true; + } + }); + + vm.endImpersonation = function () { + $http + .post("/umbraco/backoffice/Impersonator/User/EndImpersonation/") + .then(function success(response) { + vm.endImpersonatingButtonState = "busy"; + window.location.reload(); + }); + }; + } + + angular + .module("umbraco") + .controller("Impersonator.User.Controller", ImpersonatorUserController); +})(); diff --git a/src/Our.Umbraco.Impersonator/wwwroot/user.html b/src/Our.Umbraco.Impersonator/wwwroot/user.html new file mode 100644 index 0000000..dc5c048 --- /dev/null +++ b/src/Our.Umbraco.Impersonator/wwwroot/user.html @@ -0,0 +1,40 @@ +

+ + +
+ You are impersonating the current user. +
+
+ +
+
+
+ + +
+ + +
+
+
+