1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-05 23:53:21 +00:00

Compare commits

...

144 Commits

Author SHA1 Message Date
Kyle Spearrin
dca69a5428 assign BitwardenToasterService global 2018-05-31 14:41:30 -04:00
Kyle Spearrin
6b1fa0f50c intall for package lock file 2018-05-31 09:22:17 -04:00
Kyle Spearrin
3ab35dc4e0 add lowdb typings 2018-05-31 09:16:34 -04:00
Kyle Spearrin
2aa1fc1acb replace electron store with lowdb 2018-05-31 09:09:05 -04:00
Kyle Spearrin
e6643fa4cb update to electron 2.x 2018-05-31 08:12:35 -04:00
Kyle Spearrin
4848dd8502 update jslib 2018-05-24 09:01:53 -04:00
Kyle Spearrin
4b08d7b4b2 exclude export service 2018-05-17 16:22:37 -04:00
Kyle Spearrin
839189c772 update jslib 2018-05-17 16:01:08 -04:00
Kyle Spearrin
a06ff45c21 update jslib 2018-05-17 10:58:05 -04:00
Kyle Spearrin
c598efcb9f update jslib 2018-05-16 15:30:57 -04:00
Kyle Spearrin
878476d195 make logout async 2018-05-15 23:48:20 -04:00
Kyle Spearrin
82e599fc13 version bump 2018-05-14 08:49:08 -04:00
Kyle Spearrin
79b76687a0 allow settings for user and groups path on ad 2018-05-14 08:46:57 -04:00
Kyle Spearrin
af5349c4cb Merge branch 'master' of github.com:bitwarden/directory-connector 2018-05-09 16:10:42 -04:00
Kyle Spearrin
3de35b481e no need to store response variable 2018-05-09 16:10:40 -04:00
Kyle Spearrin
008bb308e7 Update README.md 2018-05-08 22:54:49 -04:00
Kyle Spearrin
9b44fc4a35 Update README.md 2018-05-08 22:52:03 -04:00
Kyle Spearrin
3d374fc792 bump version 2018-05-08 15:57:42 -04:00
Kyle Spearrin
b51c842d18 lint fix 2018-05-08 15:37:56 -04:00
Kyle Spearrin
95b595d5bf display fixes 2018-05-08 15:36:31 -04:00
Kyle Spearrin
445c3f220a handle group syncs even whenever there are no users 2018-05-08 15:11:16 -04:00
Kyle Spearrin
9644d95b9b done checking for update on reset too 2018-05-08 13:36:48 -04:00
Kyle Spearrin
eef9f151ef option to min to tray icon 2018-05-08 12:05:20 -04:00
Kyle Spearrin
6c05a4ec25 adjust menu 2018-05-08 09:40:54 -04:00
Kyle Spearrin
2ea71e1feb parameter validation 2018-05-07 23:36:14 -04:00
Kyle Spearrin
294404750d are 2018-05-07 21:41:30 -04:00
Kyle Spearrin
ada957c7a3 icns icon 2018-05-07 18:05:20 -04:00
Kyle Spearrin
cda747a1f5 icons 2018-05-07 18:01:46 -04:00
Kyle Spearrin
3f9db95eba fix returns to continues 2018-05-07 16:23:55 -04:00
Kyle Spearrin
18d77f5ff5 fix syncCount message 2018-05-07 15:14:49 -04:00
Kyle Spearrin
e7e8cfe8da fix flatten 2018-05-07 14:53:50 -04:00
Kyle Spearrin
7abc791050 welcome message on login 2018-05-07 14:51:46 -04:00
Kyle Spearrin
913098916e Merge branch 'master' of github.com:bitwarden/directory-connector 2018-05-05 00:29:23 -04:00
Kyle Spearrin
cbf1f3bd9f up lib 2018-05-05 00:29:19 -04:00
Kyle Spearrin
a0a1fe6737 Update README.md 2018-05-04 20:29:49 -04:00
Kyle Spearrin
cb2a702829 Update README.md 2018-05-04 20:29:15 -04:00
Kyle Spearrin
65f171bde0 Update README.md 2018-05-04 20:14:56 -04:00
Kyle Spearrin
85531e2133 up sub 2018-05-04 17:33:23 -04:00
Kyle Spearrin
17e058dce2 fa-fw 2018-05-04 16:38:46 -04:00
Kyle Spearrin
566f187a3d update gitignore for electron apps 2018-05-04 16:30:12 -04:00
Kyle Spearrin
85c528ce27 package update 2018-05-04 16:29:33 -04:00
Kyle Spearrin
9a87c8bffb fixes for mac build 2018-05-04 16:28:33 -04:00
Kyle Spearrin
ae931b2dde init sub on postinstall 2018-05-04 16:09:47 -04:00
Kyle Spearrin
183be70d44 storage dir adjustments 2018-05-04 16:04:05 -04:00
Kyle Spearrin
eb6e5d7a75 package app with electorn builder 2018-05-04 15:58:19 -04:00
Kyle Spearrin
e1101bdd62 more examples 2018-05-04 15:33:35 -04:00
Kyle Spearrin
954cf2c0ff filter examples 2018-05-04 15:28:50 -04:00
Kyle Spearrin
17c7806850 i18n the dashboard 2018-05-04 15:10:23 -04:00
Kyle Spearrin
2594773a12 mb on tabs 2018-05-04 14:52:45 -04:00
Kyle Spearrin
a0025e933a wire up updater 2018-05-04 14:42:46 -04:00
Kyle Spearrin
aea11afb5a query deactivated users 2018-05-03 16:33:01 -04:00
Kyle Spearrin
e40ee1e3a8 get sync status. clear sync cache 2018-05-03 15:46:20 -04:00
Kyle Spearrin
1632af3e22 organization settings 2018-05-03 14:18:47 -04:00
Kyle Spearrin
fd3793d3d7 stop and start syncing 2018-05-03 12:27:30 -04:00
Kyle Spearrin
fef65c5209 set proper page title 2018-05-03 11:33:32 -04:00
Kyle Spearrin
173ab8cf91 centralize more code 2018-05-03 11:25:34 -04:00
Kyle Spearrin
9d6cb6e044 centralize custom filters into base directory service 2018-05-03 11:09:28 -04:00
Kyle Spearrin
27c5909d7f okta filters 2018-05-03 08:49:26 -04:00
Kyle Spearrin
91b0943a8b query okta users and groups 2018-05-03 00:00:19 -04:00
Kyle Spearrin
a8b741bb22 stub out okta directory service 2018-05-02 23:31:49 -04:00
Kyle Spearrin
4cd1a8a6a0 verison is now 2.0.0 2018-05-02 22:45:05 -04:00
Kyle Spearrin
db501f9f72 sync ui updates on dashboard 2018-05-02 22:41:50 -04:00
Kyle Spearrin
95d98181fc sync logic 2018-05-02 22:00:44 -04:00
Kyle Spearrin
3981cb95fc sort results 2018-05-02 17:00:31 -04:00
Kyle Spearrin
c80fa93515 sim fixes 2018-05-02 16:36:47 -04:00
Kyle Spearrin
5e2895ce33 group enabled, disabled, and deleted users 2018-05-02 16:28:03 -04:00
Kyle Spearrin
9c5e0df719 simualtion testing from dashboard 2018-05-02 16:00:59 -04:00
Kyle Spearrin
da73fcd973 more tab with about and logout 2018-05-02 14:26:33 -04:00
Kyle Spearrin
f8ae050556 reduce menu. added gsuite file picker 2018-05-02 14:01:44 -04:00
Kyle Spearrin
d53c1211cf move placeholders 2018-05-02 13:11:32 -04:00
Kyle Spearrin
905ebd76ae styling updates 2018-05-02 12:34:47 -04:00
Kyle Spearrin
179687490c cleanup settings and set defautls for ldap ad 2018-05-01 16:52:18 -04:00
Kyle Spearrin
85aff3e75a ldap cleanup and fixes 2018-05-01 15:46:46 -04:00
Kyle Spearrin
388a69fe4e guid is 5 parts 2018-05-01 15:19:29 -04:00
Kyle Spearrin
4e9086a042 buf to arr when guid 2018-05-01 15:15:31 -04:00
Kyle Spearrin
d6707488c8 buf to guid 2018-05-01 15:05:12 -04:00
Kyle Spearrin
bbcc65f149 no current user binding 2018-05-01 14:25:54 -04:00
Kyle Spearrin
e74b99ec65 current user bind 2018-05-01 14:03:34 -04:00
Kyle Spearrin
125de3e35e new ldap.Control 2018-05-01 13:52:42 -04:00
Kyle Spearrin
9689a63bdc ldap control for deleted objects 2018-05-01 13:41:43 -04:00
Kyle Spearrin
89b468e443 query deleted objects 2018-05-01 12:24:59 -04:00
Kyle Spearrin
db6b6fe8fa bitwise check for account disabled 2018-05-01 11:55:42 -04:00
Kyle Spearrin
e5718dd742 parse guid ids 2018-05-01 11:34:28 -04:00
Kyle Spearrin
39622fe263 build import request models 2018-05-01 11:34:22 -04:00
Kyle Spearrin
1d723000a9 remove creation/revision date from entries 2018-04-30 17:19:29 -04:00
Kyle Spearrin
36101fd7dd some sync work 2018-04-30 17:01:01 -04:00
Kyle Spearrin
b8bb6cb55c process ldap directory 2018-04-30 15:06:33 -04:00
Kyle Spearrin
995249e421 format 2018-04-28 22:55:10 -04:00
Kyle Spearrin
4533dd72ae reuse access token 2018-04-28 22:43:33 -04:00
Kyle Spearrin
f5c12d9ea6 implement azure users and groups fetching for sync 2018-04-28 22:25:52 -04:00
Kyle Spearrin
ce8d58199a get users for azure service 2018-04-28 15:13:32 -04:00
Kyle Spearrin
5556a57685 gsuite directory query logic to make entries result 2018-04-28 00:13:04 -04:00
Kyle Spearrin
39f760e135 models for entries 2018-04-27 22:53:46 -04:00
Kyle Spearrin
e8351c3246 cleanup 2018-04-27 22:41:39 -04:00
Kyle Spearrin
35bf1f0f77 save on destroy 2018-04-27 22:40:10 -04:00
Kyle Spearrin
87039fa784 conditional settings for ldap ad 2018-04-27 22:35:32 -04:00
Kyle Spearrin
dea8e48895 wire up directory services to settings 2018-04-27 19:31:15 -04:00
Kyle Spearrin
4145de7662 layout styling 2018-04-27 18:22:27 -04:00
Kyle Spearrin
2800c2f077 settings configuration 2018-04-27 17:16:54 -04:00
Kyle Spearrin
e7787ea95c stub out directory service auth/querying 2018-04-27 14:13:22 -04:00
Kyle Spearrin
e7809b405d tabs 2018-04-26 17:26:19 -04:00
Kyle Spearrin
b04b3ef924 setup bootstrap scss 2018-04-26 17:09:19 -04:00
Kyle Spearrin
6876b905cd cleanup imports 2018-04-26 16:19:25 -04:00
Kyle Spearrin
371101bf69 remove unneeded features 2018-04-26 16:17:34 -04:00
Kyle Spearrin
e17ecf967d email address string 2018-04-26 16:02:21 -04:00
Kyle Spearrin
b0705a911d wire up message service to menu 2018-04-26 16:00:47 -04:00
Kyle Spearrin
99c7f619e0 main messaging service to jslib 2018-04-26 15:45:52 -04:00
Kyle Spearrin
c4a37b2a85 added i18n strings 2018-04-26 15:29:57 -04:00
Kyle Spearrin
c12f40ecf7 debug main and renderer 2018-04-26 13:09:16 -04:00
Kyle Spearrin
fe6055c402 update sub 2018-04-26 00:43:28 -04:00
Kyle Spearrin
d2b36c4d23 update sub 2018-04-26 00:20:20 -04:00
Kyle Spearrin
fdd29190a4 use concurrently tool 2018-04-25 16:48:34 -04:00
Kyle Spearrin
6d31d0a60e node crypto service 2018-04-25 15:52:44 -04:00
Kyle Spearrin
a4cb908390 menu and main keytar storage service 2018-04-25 15:49:10 -04:00
Kyle Spearrin
b8d3c35e34 dont set crypto keys 2018-04-25 12:43:06 -04:00
Kyle Spearrin
48bde677bc routing with guards 2018-04-25 12:30:27 -04:00
Kyle Spearrin
cafb7da93e remove sync dependency 2018-04-25 12:08:34 -04:00
Kyle Spearrin
ad6c3cb132 wire up services 2018-04-25 09:01:29 -04:00
Kyle Spearrin
e1e532ed91 stub out electron app 2018-04-24 17:31:40 -04:00
Kyle Spearrin
2afbeb1c10 stub out directory services 2018-04-18 21:10:28 -04:00
Kyle Spearrin
598db5c83b add organization duo 2fa type 2018-04-03 16:16:48 -04:00
Kyle Spearrin
ceddb83d2d remove full namespace 2018-03-31 11:06:24 -04:00
Kyle Spearrin
4e70378b46 uppercase more bitwarden 2018-02-27 16:31:59 -05:00
Kyle Spearrin
5cd7f3568e Uppercase Bitwarden 2018-02-27 14:28:51 -05:00
Kyle Spearrin
2c2f1921c1 filter users from filtered groups 2017-12-12 15:55:24 -05:00
Kyle Spearrin
1e5e28e2b6 use proper entry for group user search 2017-12-11 11:29:08 -05:00
Kyle Spearrin
41f8263a7c setup reference file manually. 2017-12-07 12:17:26 -05:00
Kyle Spearrin
818a4db96e update libs. convert service to new csproj format 2017-12-07 11:53:17 -05:00
Kyle Spearrin
51ab260fe6 remove framework netcore2.0 target for now
- setup installer does not pick correct output
2017-12-06 12:31:27 -05:00
Kyle Spearrin
be393f7a63 client side filtering for azure ad 2017-12-05 09:10:24 -05:00
Kyle Spearrin
28c0509886 null checks 2017-11-30 15:50:05 -05:00
Kyle Spearrin
835c9f9cac added clear cache to menu option 9 2017-11-10 15:36:09 -05:00
Kyle Spearrin
6e4e78c30e proper default namespace 2017-10-24 17:21:19 -04:00
Kyle Spearrin
7d3ea444f4 convert projects to netstandard lib & netcore app 2017-10-24 17:13:56 -04:00
Kyle Spearrin
fef8bd1e00 switch to bearer auth headers 2017-10-17 08:39:14 -04:00
Kyle Spearrin
2fa1b52a36 permission rules 2017-08-28 17:05:38 -04:00
Kyle Spearrin
41d0b53898 users rule for UAC 2017-08-28 16:15:54 -04:00
Kyle Spearrin
259c1bbce6 remove gui app 2017-08-28 14:58:03 -04:00
Kyle Spearrin
73b6a29c56 update gsuite sdk, move installer, remove GUI app 2017-08-28 14:53:44 -04:00
Kyle Spearrin
0835b748af write search path and filter to screen for LDAP 2017-08-17 19:55:55 -04:00
Kyle Spearrin
c6abbf61dc fix bug in app 2017-08-15 14:37:18 -04:00
Kyle Spearrin
519171d241 added support additional two factor providers during login 2017-08-15 14:32:40 -04:00
Kyle Spearrin
9803a55ca3 correct client id on refresh 2017-08-15 13:18:44 -04:00
Kyle Spearrin
305a66d64d G Suite with a space 2017-05-25 12:22:54 -04:00
186 changed files with 16447 additions and 11311 deletions

16
.editorconfig Normal file
View File

@@ -0,0 +1,16 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
quote_type = single
# Set default charset
[*.{js,ts,scss,html}]
charset = utf-8
indent_style = space
indent_size = 4

272
.gitignore vendored
View File

@@ -1,261 +1,13 @@
## 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/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# 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
project.fragment.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
.vs
.idea
node_modules
npm-debug.log
vwd.webinfo
dist/
*.crx
*.pem
build/
yarn-error.log
.DS_Store
*.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
*.jfm
*.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
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
*.provisionprofile

4
.gitmodules vendored Normal file
View File

@@ -0,0 +1,4 @@
[submodule "jslib"]
path = jslib
url = https://github.com/bitwarden/jslib.git
branch = master

37
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,37 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Electron: Main",
"protocol": "inspector",
"cwd": "${workspaceRoot}/build",
"runtimeArgs": [
"--remote-debugging-port=9223",
"."
],
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"sourceMaps": true
},
{
"name": "Electron: Renderer",
"type": "chrome",
"request": "attach",
"port": 9223,
"webRoot": "${workspaceFolder}/build",
"sourceMaps": true
}
],
"compounds": [
{
"name": "Electron: All",
"configurations": [
"Electron: Main",
"Electron: Renderer"
]
}
]
}

13
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,13 @@
Code contributions are welcome! Please commit any pull requests against the `master` branch.
# Localization (l10n)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-desktop/localized.svg)](https://crowdin.com/project/bitwarden-desktop)
We use a translation tool called [Crowdin](https://crowdin.com) to help manage our localization efforts across many different languages.
If you are interested in helping translate the Bitwarden desktop app into another language (or make a translation correction), please register an account at Crowdin and join our project here: https://crowdin.com/project/bitwarden-desktop
If the language that you are interested in translating is not already listed, create a new account on Crowdin, join the project, and contact the project owner (https://crowdin.com/mail/compose/kspearrin).
You can read Crowdin's getting started guide for translators here: https://support.crowdin.com/crowdin-intro/

5
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,5 @@
<!--
Please do not submit feature requests. The [Community Forums][1] has a
section for submitting, voting for, and discussing product feature requests.
[1]: https://community.bitwarden.com
-->

View File

@@ -1,5 +1,5 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
@@ -7,15 +7,17 @@
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -60,7 +72,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -629,33 +631,44 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
GNU General Public License for more details.
You should have received a copy of the GNU Affero General Public License
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@@ -1,29 +1,39 @@
[![appveyor build](https://ci.appveyor.com/api/projects/status/github/bitwarden/directory-connector?branch=master&svg=true)](https://ci.appveyor.com/project/bitwarden/directory-connector)
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
# bitwarden Directory Connector
# Bitwarden Directory Connector
The bitwarden Directory Connector is a command line application used to connect your bitwarden enterprise organization to an existing directory of users and groups.
It is written in C# with the .NET Framework. It consists of a console application and an optional windows service to run syncs in the background on a specified interval.
The Bitwarden Directory Connector is a a desktop application used to sync your Bitwarden enterprise organization to an existing directory of users and groups.
Supported directories:
- Active Directory
- Azure Active Directory
- GSuite (Google)
- Any other LDAP-based directory
- Azure Active Directory
- G Suite (Google)
- Okta
<img src="https://i.imgur.com/IdqS0se.png" alt="" width="680" height="479" />
The application is written using Electron with Angular and installs on Windows, macOS, and Linux distributions.
<a href="https://help.bitwarden.com/article/directory-sync/#download-and-install"><img src="https://imgur.com/SLv9paA.png" width="500" height="113"></a>
![Directory Connector](http://imgur.com/I6FjN4j.png "Dashboard")
# Build/Run
**Requirements**
- [Visual Studio](https://www.visualstudio.com/)
- [Node.js](https://nodejs.org/)
- Windows users: To compile the native node modules used in the app you will need the Visual C++ toolset, available through the standard Visual Studio installer (recommended) or by installing [`windows-build-tools`](https://github.com/felixrieseberg/windows-build-tools) through `npm`. See more at [Compiling native Addon modules](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules).
Open `bitwarden-directory-connector.sln` or `bitwarden-directory-connector-noinstaller.sln`. After restoring the nuget packages, you can build and run the application.
**Run the app**
```bash
npm install
npm run electron
```
# Contribute
Code contributions are welcome! Visual Studio is required to work on this project. Please commit any pull requests against the `master` branch.
Code contributions are welcome! Please commit any pull requests against the `master` branch. Learn more about how to contribute by reading the [`CONTRIBUTING.md`](CONTRIBUTING.md) file.
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.

View File

@@ -1,4 +1,4 @@
bitwarden believes that working with security researchers across the globe is crucial to keeping our
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
users safe. If you believe you've found a security issue in our product or service, we encourage you to
notify us. We welcome working with you to resolve the issue promptly. Thanks in advance!
@@ -16,7 +16,7 @@ notify us. We welcome working with you to resolve the issue promptly. Thanks in
# In-scope
- Security issues in any current release of bitwarden. This includes the web vault, browser extension,
- Security issues in any current release of Bitwarden. This includes the web vault, browser extension,
and mobile apps (iOS and Android). Product downloads are available at https://bitwarden.com. Source
code is available at https://github.com/bitwarden.
@@ -24,14 +24,14 @@ notify us. We welcome working with you to resolve the issue promptly. Thanks in
The following bug classes are out-of scope:
- Bugs that are already reported on any of bitwarden's issue trackers (https://github.com/bitwarden),
- Bugs that are already reported on any of Bitwarden's issue trackers (https://github.com/bitwarden),
or that we already know of. Note that some of our issue tracking is private.
- Issues in an upstream software dependency (ex: Xamarin, ASP.NET) which are already reported to the
upstream maintainer.
- Attacks requiring physical access to a user's device.
- Self-XSS
- Issues related to software or protocols not under bitwarden's control
- Vulnerabilities in outdated versions of bitwarden
- Issues related to software or protocols not under Bitwarden's control
- Vulnerabilities in outdated versions of Bitwarden
- Missing security best practices that do not directly lead to a vulnerability
- Issues that do not have any impact on the general public
@@ -39,7 +39,7 @@ While researching, we'd like to ask you to refrain from:
- Denial of service
- Spamming
- Social engineering (including phishing) of bitwarden staff or contractors
- Any physical attempts against bitwarden property or data centers
- Social engineering (including phishing) of Bitwarden staff or contractors
- Any physical attempts against Bitwarden property or data centers
Thank you for helping keep bitwarden and our users safe!
Thank you for helping keep Bitwarden and our users safe!

View File

@@ -1,40 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "src\Console\Console.csproj", "{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{AE082484-A34C-4B3A-A69F-49E5EF298B27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "src\Service\Service.csproj", "{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{C4A631EE-19DF-4A10-8526-CB6996EFA853}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Release|Any CPU.Build.0 = Release|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Release|Any CPU.Build.0 = Release|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Release|Any CPU.Build.0 = Release|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -1,44 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26430.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "src\Console\Console.csproj", "{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{AE082484-A34C-4B3A-A69F-49E5EF298B27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Service", "src\Service\Service.csproj", "{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{C4A631EE-19DF-4A10-8526-CB6996EFA853}"
EndProject
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Setup", "src\Setup\Setup.vdproj", "{4D852DF8-9327-43D0-93AB-FA68D4F3414B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}.Release|Any CPU.Build.0 = Release|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE082484-A34C-4B3A-A69F-49E5EF298B27}.Release|Any CPU.Build.0 = Release|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8FD8CED-5510-4EBD-AACE-5D3CBB7516DB}.Release|Any CPU.Build.0 = Release|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4A631EE-19DF-4A10-8526-CB6996EFA853}.Release|Any CPU.Build.0 = Release|Any CPU
{4D852DF8-9327-43D0-93AB-FA68D4F3414B}.Debug|Any CPU.ActiveCfg = Debug
{4D852DF8-9327-43D0-93AB-FA68D4F3414B}.Release|Any CPU.ActiveCfg = Release
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

1
jslib Submodule

Submodule jslib added at e0d5a4d8b7

11186
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

174
package.json Normal file
View File

@@ -0,0 +1,174 @@
{
"name": "bitwarden-directory-connector",
"productName": "Bitwarden Directory Connector",
"description": "Sync your user directory to your Bitwarden organization.",
"version": "0.0.0",
"keywords": [
"bitwarden",
"password",
"vault",
"password manager"
],
"author": "8bit Solutions LLC <hello@bitwarden.com> (https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"repository": {
"type": "git",
"url": "https://github.com/bitwarden/directory-connector"
},
"license": "GPL-3.0",
"scripts": {
"sub:init": "git submodule update --init --recursive",
"sub:update": "git submodule update --remote",
"sub:pull": "git submodule foreach git pull origin master",
"sub:commit": "npm run sub:pull && git commit -am \"update submodule\"",
"postinstall": "./node_modules/.bin/electron-rebuild && npm run sub:init",
"lint": "tslint src/**/*.ts || true",
"lint:fix": "tslint src/**/*.ts --fix",
"build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
"build:main": "webpack --config webpack.main.js",
"build:renderer": "webpack --config webpack.renderer.js",
"build:renderer:watch": "webpack --config webpack.renderer.js --watch",
"electron": "npm run build:main && concurrently -k -n Main,Rend -c yellow,cyan \"electron --inspect=5858 ./build --watch\" \"npm run build:renderer:watch\"",
"clean:dist": "rimraf ./dist/*",
"pack:lin": "npm run clean:dist && build --linux --x64 -p never",
"pack:mac": "npm run clean:dist && build --mac -p never",
"pack:win": "npm run clean:dist && build --win --x64 --ia32 -p never -c.win.certificateSubjectName=\"8bit Solutions LLC\"",
"pack:win:ci": "npm run clean:dist && build --win --x64 --ia32 -p never",
"dist:lin": "npm run build && npm run pack:lin",
"dist:mac": "npm run build && npm run pack:mac",
"dist:win": "npm run build && npm run pack:win",
"dist:win:ci": "npm run build && npm run pack:win:ci",
"publish:lin": "npm run build && npm run clean:dist && build --linux --x64 -p always",
"publish:mac": "npm run build && npm run clean:dist && build --mac -p always",
"publish:win": "npm run build && npm run clean:dist && build --win --x64 --ia32 -p always -c.win.certificateSubjectName=\"8bit Solutions LLC\""
},
"build": {
"appId": "com.bitwarden.directory-connector",
"copyright": "Copyright © 2015-2018 8bit Solutions LLC",
"directories": {
"buildResources": "resources",
"output": "dist",
"app": "build"
},
"mac": {
"category": "public.app-category.productivity",
"target": [
"dmg",
"zip"
]
},
"win": {
"target": [
"portable",
"nsis"
]
},
"linux": {
"category": "Utility",
"synopsis": "Sync your user directory to your Bitwarden organization.",
"target": [
"AppImage"
]
},
"dmg": {
"artifactName": "Bitwarden-Connector-${version}.${ext}",
"icon": "dmg.icns",
"contents": [
{
"x": 150,
"y": 185,
"type": "file"
},
{
"x": 390,
"y": 180,
"type": "link",
"path": "/Applications"
}
],
"window": {
"width": 540,
"height": 380
}
},
"nsis": {
"oneClick": false,
"perMachine": true,
"allowToChangeInstallationDirectory": true,
"artifactName": "Bitwarden-Connector-Installer-${version}.${ext}",
"uninstallDisplayName": "${productName}",
"deleteAppDataOnUninstall": true
},
"portable": {
"artifactName": "Bitwarden-Connector-Portable-${version}.${ext}"
},
"appImage": {
"artifactName": "Bitwarden-Connector-${version}-${arch}.${ext}"
}
},
"devDependencies": {
"@angular/compiler-cli": "5.2.0",
"@microsoft/microsoft-graph-types": "^1.2.0",
"@ngtools/webpack": "1.10.2",
"@types/keytar": "^4.0.1",
"@types/ldapjs": "^1.0.3",
"@types/lowdb": "^1.0.1",
"@types/lunr": "2.1.5",
"@types/node": "8.0.19",
"@types/node-forge": "0.7.1",
"@types/webcrypto": "0.0.28",
"clean-webpack-plugin": "^0.1.17",
"concurrently": "3.5.1",
"copy-webpack-plugin": "^4.2.0",
"css-loader": "^0.28.7",
"electron": "2.0.2",
"electron-builder": "^20.8.1",
"electron-rebuild": "1.7.3",
"electron-reload": "1.2.2",
"extract-text-webpack-plugin": "^3.0.1",
"file-loader": "^1.1.5",
"font-awesome": "4.7.0",
"google-fonts-webpack-plugin": "^0.4.4",
"html-loader": "^0.5.1",
"html-webpack-plugin": "^2.30.1",
"node-loader": "^0.6.0",
"node-sass": "^4.7.2",
"rimraf": "^2.6.2",
"sass-loader": "^6.0.6",
"ts-loader": "^3.5.0",
"tslint": "^5.9.1",
"tslint-loader": "^3.5.3",
"typescript": "^2.7.1",
"webpack": "^3.10.0",
"webpack-merge": "^4.1.0",
"webpack-node-externals": "^1.6.0"
},
"dependencies": {
"@angular/animations": "5.2.0",
"@angular/common": "5.2.0",
"@angular/compiler": "5.2.0",
"@angular/core": "5.2.0",
"@angular/forms": "5.2.0",
"@angular/http": "5.2.0",
"@angular/platform-browser": "5.2.0",
"@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.2.0",
"@angular/upgrade": "5.2.0",
"@microsoft/microsoft-graph-client": "1.0.0",
"@okta/okta-sdk-nodejs": "1.1.0",
"angular2-toaster": "4.0.2",
"angulartics2": "5.0.1",
"bootstrap": "4.1.0",
"core-js": "2.4.1",
"electron-log": "2.2.14",
"electron-updater": "2.21.4",
"googleapis": "29.0.0",
"keytar": "4.1.0",
"ldapjs": "1.0.2",
"lowdb": "1.0.0",
"lunr": "2.1.6",
"node-forge": "0.7.1",
"rxjs": "5.5.6",
"zone.js": "0.8.19"
}
}

BIN
resources/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
resources/dmg.icns Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

BIN
resources/icon.icns Normal file

Binary file not shown.

BIN
resources/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
resources/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
resources/icons/16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

BIN
resources/icons/256x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
resources/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 B

BIN
resources/icons/48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B

BIN
resources/icons/512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
resources/icons/64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@@ -1,72 +0,0 @@
namespace App
{
partial class Alert
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label1
//
this.label1.Location = new System.Drawing.Point(12, 20);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(279, 77);
this.label1.TabIndex = 0;
this.label1.Text = "label1";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// button1
//
this.button1.Location = new System.Drawing.Point(15, 116);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(276, 23);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Alert
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(303, 162);
this.Controls.Add(this.button1);
this.Controls.Add(this.label1);
this.Name = "Alert";
this.Text = "Alert";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button button1;
}
}

View File

@@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace App
{
public partial class Alert : Form
{
public bool ButtonClicked { get; set; }
public Alert(string title, string label, string buttonText = "Ok")
{
InitializeComponent();
Text = title;
label1.Text = label;
button1.Text = buttonText;
}
private void button1_Click(object sender, EventArgs e)
{
ButtonClicked = true;
DialogResult = DialogResult.OK;
Close();
}
}
}

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -1,106 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C4A631EE-19DF-4A10-8526-CB6996EFA853}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>App</RootNamespace>
<AssemblyName>App</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Alert.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Alert.Designer.cs">
<DependentUpon>Alert.cs</DependentUpon>
</Compile>
<Compile Include="Main.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Main.Designer.cs">
<DependentUpon>Main.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Prompt.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Prompt.Designer.cs">
<DependentUpon>Prompt.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Alert.resx">
<DependentUpon>Alert.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Main.resx">
<DependentUpon>Main.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Prompt.resx">
<DependentUpon>Prompt.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj">
<Project>{ae082484-a34c-4b3a-a69f-49e5ef298b27}</Project>
<Name>Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

422
src/App/Main.Designer.cs generated
View File

@@ -1,422 +0,0 @@
namespace App
{
partial class Main
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.tabPage3 = new System.Windows.Forms.TabPage();
this.tabPage4 = new System.Windows.Forms.TabPage();
this.tabPage5 = new System.Windows.Forms.TabPage();
this.tabPage6 = new System.Windows.Forms.TabPage();
this.label1 = new System.Windows.Forms.Label();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.label2 = new System.Windows.Forms.Label();
this.linkLabel1 = new System.Windows.Forms.LinkLabel();
this.panel1 = new System.Windows.Forms.Panel();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label6 = new System.Windows.Forms.Label();
this.textBox2 = new System.Windows.Forms.TextBox();
this.button4 = new System.Windows.Forms.Button();
this.radioButton1 = new System.Windows.Forms.RadioButton();
this.radioButton2 = new System.Windows.Forms.RadioButton();
this.radioButton3 = new System.Windows.Forms.RadioButton();
this.loginButton = new System.Windows.Forms.Button();
this.label7 = new System.Windows.Forms.Label();
this.usernameTextBox = new System.Windows.Forms.TextBox();
this.label8 = new System.Windows.Forms.Label();
this.passwordTextBox = new System.Windows.Forms.MaskedTextBox();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.tabPage2.SuspendLayout();
this.tabPage4.SuspendLayout();
this.tabPage5.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Controls.Add(this.tabPage3);
this.tabControl1.Controls.Add(this.tabPage4);
this.tabControl1.Controls.Add(this.tabPage5);
this.tabControl1.Controls.Add(this.tabPage6);
this.tabControl1.Location = new System.Drawing.Point(12, 132);
this.tabControl1.Multiline = true;
this.tabControl1.Name = "tabControl1";
this.tabControl1.Padding = new System.Drawing.Point(10, 10);
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(579, 368);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.panel1);
this.tabPage1.Location = new System.Drawing.Point(4, 36);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(571, 328);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Log In";
this.tabPage1.UseVisualStyleBackColor = true;
//
// tabPage2
//
this.tabPage2.Controls.Add(this.radioButton3);
this.tabPage2.Controls.Add(this.radioButton2);
this.tabPage2.Controls.Add(this.radioButton1);
this.tabPage2.Location = new System.Drawing.Point(4, 36);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(571, 328);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Directory Settings";
this.tabPage2.UseVisualStyleBackColor = true;
//
// tabPage3
//
this.tabPage3.Location = new System.Drawing.Point(4, 36);
this.tabPage3.Name = "tabPage3";
this.tabPage3.Padding = new System.Windows.Forms.Padding(3);
this.tabPage3.Size = new System.Drawing.Size(571, 328);
this.tabPage3.TabIndex = 2;
this.tabPage3.Text = "Sync Settings";
this.tabPage3.UseVisualStyleBackColor = true;
//
// tabPage4
//
this.tabPage4.Controls.Add(this.button4);
this.tabPage4.Controls.Add(this.textBox2);
this.tabPage4.Controls.Add(this.label6);
this.tabPage4.Controls.Add(this.textBox1);
this.tabPage4.Controls.Add(this.label5);
this.tabPage4.Location = new System.Drawing.Point(4, 36);
this.tabPage4.Name = "tabPage4";
this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
this.tabPage4.Size = new System.Drawing.Size(571, 328);
this.tabPage4.TabIndex = 3;
this.tabPage4.Text = "Environment";
this.tabPage4.UseVisualStyleBackColor = true;
//
// tabPage5
//
this.tabPage5.Controls.Add(this.label4);
this.tabPage5.Controls.Add(this.label3);
this.tabPage5.Controls.Add(this.button3);
this.tabPage5.Controls.Add(this.button2);
this.tabPage5.Location = new System.Drawing.Point(4, 36);
this.tabPage5.Name = "tabPage5";
this.tabPage5.Padding = new System.Windows.Forms.Padding(3);
this.tabPage5.Size = new System.Drawing.Size(571, 328);
this.tabPage5.TabIndex = 4;
this.tabPage5.Text = "Service Worker";
this.tabPage5.UseVisualStyleBackColor = true;
//
// tabPage6
//
this.tabPage6.Location = new System.Drawing.Point(4, 36);
this.tabPage6.Name = "tabPage6";
this.tabPage6.Padding = new System.Windows.Forms.Padding(3);
this.tabPage6.Size = new System.Drawing.Size(571, 328);
this.tabPage6.TabIndex = 5;
this.tabPage6.Text = "Sync";
this.tabPage6.UseVisualStyleBackColor = true;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 15F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.Location = new System.Drawing.Point(85, 12);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(274, 25);
this.label1.TabIndex = 1;
this.label1.Text = "bitwarden Directory Connector";
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(12, 12);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(64, 62);
this.pictureBox1.TabIndex = 2;
this.pictureBox1.TabStop = false;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(87, 41);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(69, 13);
this.label2.TabIndex = 3;
this.label2.Text = "Version 1.0.0";
//
// linkLabel1
//
this.linkLabel1.AutoSize = true;
this.linkLabel1.Location = new System.Drawing.Point(87, 59);
this.linkLabel1.Name = "linkLabel1";
this.linkLabel1.Size = new System.Drawing.Size(112, 13);
this.linkLabel1.TabIndex = 4;
this.linkLabel1.TabStop = true;
this.linkLabel1.Text = "https://bitwarden.com";
this.linkLabel1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// panel1
//
this.panel1.Controls.Add(this.passwordTextBox);
this.panel1.Controls.Add(this.loginButton);
this.panel1.Controls.Add(this.label7);
this.panel1.Controls.Add(this.usernameTextBox);
this.panel1.Controls.Add(this.label8);
this.panel1.Location = new System.Drawing.Point(6, 6);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(559, 151);
this.panel1.TabIndex = 0;
//
// button2
//
this.button2.Location = new System.Drawing.Point(141, 96);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(271, 23);
this.button2.TabIndex = 0;
this.button2.Text = "Start Service";
this.button2.UseVisualStyleBackColor = true;
//
// button3
//
this.button3.Location = new System.Drawing.Point(141, 125);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(271, 23);
this.button3.TabIndex = 1;
this.button3.Text = "Stop Service";
this.button3.UseVisualStyleBackColor = true;
//
// label3
//
this.label3.Location = new System.Drawing.Point(141, 20);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(271, 18);
this.label3.TabIndex = 2;
this.label3.Text = "Current Status";
this.label3.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// label4
//
this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label4.Location = new System.Drawing.Point(141, 50);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(271, 23);
this.label4.TabIndex = 3;
this.label4.Text = "Running";
this.label4.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(3, 20);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(94, 13);
this.label5.TabIndex = 0;
this.label5.Text = "API Endpoint URL";
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(6, 36);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(294, 20);
this.textBox1.TabIndex = 1;
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(3, 70);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(111, 13);
this.label6.TabIndex = 2;
this.label6.Text = "Identity Endpoint URL";
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(6, 86);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(294, 20);
this.textBox2.TabIndex = 3;
//
// button4
//
this.button4.Location = new System.Drawing.Point(6, 126);
this.button4.Name = "button4";
this.button4.Size = new System.Drawing.Size(75, 23);
this.button4.TabIndex = 4;
this.button4.Text = "Save";
this.button4.UseVisualStyleBackColor = true;
//
// radioButton1
//
this.radioButton1.AutoSize = true;
this.radioButton1.Location = new System.Drawing.Point(6, 49);
this.radioButton1.Name = "radioButton1";
this.radioButton1.Size = new System.Drawing.Size(100, 17);
this.radioButton1.TabIndex = 0;
this.radioButton1.TabStop = true;
this.radioButton1.Text = "Active Directory";
this.radioButton1.UseVisualStyleBackColor = true;
//
// radioButton2
//
this.radioButton2.AutoSize = true;
this.radioButton2.Location = new System.Drawing.Point(6, 72);
this.radioButton2.Name = "radioButton2";
this.radioButton2.Size = new System.Drawing.Size(130, 17);
this.radioButton2.TabIndex = 1;
this.radioButton2.TabStop = true;
this.radioButton2.Text = "Azure Active Directory";
this.radioButton2.UseVisualStyleBackColor = true;
//
// radioButton3
//
this.radioButton3.AutoSize = true;
this.radioButton3.Location = new System.Drawing.Point(6, 95);
this.radioButton3.Name = "radioButton3";
this.radioButton3.Size = new System.Drawing.Size(82, 17);
this.radioButton3.TabIndex = 2;
this.radioButton3.TabStop = true;
this.radioButton3.Text = "Other LDAP";
this.radioButton3.UseVisualStyleBackColor = true;
//
// loginButton
//
this.loginButton.Location = new System.Drawing.Point(3, 105);
this.loginButton.Name = "loginButton";
this.loginButton.Size = new System.Drawing.Size(75, 23);
this.loginButton.TabIndex = 9;
this.loginButton.Text = "Log In";
this.loginButton.UseVisualStyleBackColor = true;
this.loginButton.Click += new System.EventHandler(this.loginButton_Click);
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(0, 63);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(53, 13);
this.label7.TabIndex = 7;
this.label7.Text = "Password";
//
// usernameTextBox
//
this.usernameTextBox.Location = new System.Drawing.Point(3, 39);
this.usernameTextBox.Name = "usernameTextBox";
this.usernameTextBox.Size = new System.Drawing.Size(294, 20);
this.usernameTextBox.TabIndex = 6;
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(0, 23);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(55, 13);
this.label8.TabIndex = 5;
this.label8.Text = "Username";
//
// passwordTextBox
//
this.passwordTextBox.Location = new System.Drawing.Point(3, 79);
this.passwordTextBox.Name = "passwordTextBox";
this.passwordTextBox.Size = new System.Drawing.Size(294, 20);
this.passwordTextBox.TabIndex = 10;
this.passwordTextBox.UseSystemPasswordChar = true;
//
// Main
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(603, 512);
this.Controls.Add(this.linkLabel1);
this.Controls.Add(this.label2);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.label1);
this.Controls.Add(this.tabControl1);
this.Name = "Main";
this.Text = "Main";
this.Load += new System.EventHandler(this.Main_Load);
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage2.ResumeLayout(false);
this.tabPage2.PerformLayout();
this.tabPage4.ResumeLayout(false);
this.tabPage4.PerformLayout();
this.tabPage5.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.TabPage tabPage3;
private System.Windows.Forms.TabPage tabPage4;
private System.Windows.Forms.TabPage tabPage5;
private System.Windows.Forms.TabPage tabPage6;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.LinkLabel linkLabel1;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.MaskedTextBox passwordTextBox;
private System.Windows.Forms.Button loginButton;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.TextBox usernameTextBox;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.RadioButton radioButton3;
private System.Windows.Forms.RadioButton radioButton2;
private System.Windows.Forms.RadioButton radioButton1;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label5;
}
}

View File

@@ -1,86 +0,0 @@
using Bit.Core.Models;
using Bit.Core.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace App
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
}
private void Main_Load(object sender, EventArgs e)
{
}
private async void loginButton_Click(object sender, EventArgs e)
{
var result = await AuthService.Instance.LogInAsync(usernameTextBox.Text, passwordTextBox.Text);
if(result.TwoFactorRequired)
{
string token = null;
using(var prompt = new Prompt("Verification Code", "Enter your two-step verification code", "Submit"))
{
var promptResult = prompt.ShowDialog();
if(promptResult == DialogResult.OK)
{
token = prompt.ReturnValue;
}
}
result = await AuthService.Instance.LogInTwoFactorWithHashAsync(token, usernameTextBox.Text,
result.MasterPasswordHash);
}
if(result.Success && result.Organizations.Count > 1)
{
Organization org = null;
var orgs = new Dictionary<string, string>();
for(int i = 0; i < result.Organizations.Count; i++)
{
orgs.Add(result.Organizations[i].Id, result.Organizations[i].Name);
}
// TODO: alert about org
if(org == null)
{
result.Success = false;
result.ErrorMessage = "Organization not found.";
AuthService.Instance.LogOut();
}
else
{
SettingsService.Instance.Organization = org;
}
}
if(result.Success)
{
}
else
{
using(var prompt = new Alert("Error", result.ErrorMessage))
{
var promptResult = prompt.ShowDialog();
}
}
passwordTextBox.Text = null;
}
}
}

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace App
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Main());
}
}
}

View File

@@ -1,83 +0,0 @@
namespace App
{
partial class Prompt
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label1
//
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(224, 41);
this.label1.TabIndex = 0;
this.label1.Text = "label1";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(12, 63);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(224, 20);
this.textBox1.TabIndex = 1;
//
// button1
//
this.button1.Location = new System.Drawing.Point(12, 89);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(224, 23);
this.button1.TabIndex = 2;
this.button1.Text = "Ok";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Prompt
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(248, 135);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.label1);
this.Name = "Prompt";
this.Text = "Prompt";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
}
}

View File

@@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace App
{
public partial class Prompt : Form
{
public string ReturnValue { get; set; }
public Prompt(string title, string label, string buttonText)
{
InitializeComponent();
Text = title;
label1.Text = label;
button1.Text = buttonText;
}
private void button1_Click(object sender, EventArgs e)
{
ReturnValue = textBox1.Text;
DialogResult = DialogResult.OK;
Close();
}
}
}

View File

@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("App")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("App")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c4a631ee-19df-4a10-8526-cb6996efa853")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,71 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace App.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("App.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -1,30 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace App.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

View File

@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DD4E5CD2-C9DD-4912-9A25-1600A07BF8C2}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>Bit.Console</RootNamespace>
<AssemblyName>Console</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj">
<Project>{ae082484-a34c-4b3a-a69f-49e5ef298b27}</Project>
<Name>Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Console")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Console")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("dd4e5cd2-c9dd-4912-9a25-1600a07bf8c2")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,135 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AE082484-A34C-4B3A-A69F-49E5EF298B27}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Bit.Core</RootNamespace>
<AssemblyName>Core</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
<HintPath>..\..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="Google.Apis, Version=1.25.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\..\packages\Google.Apis.1.25.0\lib\net45\Google.Apis.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Admin.Directory.directory_v1, Version=1.25.0.844, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\..\packages\Google.Apis.Admin.Directory.directory_v1.1.25.0.844\lib\net45\Google.Apis.Admin.Directory.directory_v1.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth, Version=1.25.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\..\packages\Google.Apis.Auth.1.25.0\lib\net45\Google.Apis.Auth.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Auth.PlatformServices, Version=1.25.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\..\packages\Google.Apis.Auth.1.25.0\lib\net45\Google.Apis.Auth.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.Core, Version=1.25.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\..\packages\Google.Apis.Core.1.25.0\lib\net45\Google.Apis.Core.dll</HintPath>
</Reference>
<Reference Include="Google.Apis.PlatformServices, Version=1.25.0.0, Culture=neutral, PublicKeyToken=4b01fa6e34db77ab, processorArchitecture=MSIL">
<HintPath>..\..\packages\Google.Apis.1.25.0\lib\net45\Google.Apis.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Graph, Version=1.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Graph.1.3.0\lib\net45\Microsoft.Graph.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Graph.Core, Version=1.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Graph.Core.1.4.0\lib\net45\Microsoft.Graph.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.13.9.1126, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.9\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform, Version=3.13.9.1126, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.9\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.DirectoryServices.Protocols" />
<Reference Include="System.Security" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="Zlib.Portable, Version=1.11.0.0, Culture=neutral, PublicKeyToken=431cba815f6a8b5b, processorArchitecture=MSIL">
<HintPath>..\..\packages\Zlib.Portable.Signed.1.11.0\lib\portable-net4+sl5+wp8+win8+wpa81+MonoTouch+MonoAndroid\Zlib.Portable.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Enums\DirectoryType.cs" />
<Compile Include="Enums\OrganizationUserType.cs" />
<Compile Include="Enums\OrganizationUserStatusType.cs" />
<Compile Include="Enums\UserAccountControl.cs" />
<Compile Include="Installer.cs" />
<Compile Include="Models\ApiError.cs" />
<Compile Include="Models\ApiResult.cs" />
<Compile Include="Models\Entry.cs" />
<Compile Include="Models\GSuiteConfiguration.cs" />
<Compile Include="Models\ImportRequest.cs" />
<Compile Include="Models\AzureConfiguration.cs" />
<Compile Include="Models\ServerConfiguration.cs" />
<Compile Include="Models\Organization.cs" />
<Compile Include="Models\ProfileOrganizationResponse.cs" />
<Compile Include="Models\SyncConfiguration.cs" />
<Compile Include="Models\LdapConfiguration.cs" />
<Compile Include="Models\LoginResult.cs" />
<Compile Include="Models\ErrorResponse.cs" />
<Compile Include="Models\EncryptedData.cs" />
<Compile Include="Models\SyncResult.cs" />
<Compile Include="Models\TokenRequest.cs" />
<Compile Include="Models\ProfileResponse.cs" />
<Compile Include="Models\TokenResponse.cs" />
<Compile Include="Services\ApiService.cs" />
<Compile Include="Services\GSuiteDirectoryService.cs" />
<Compile Include="Services\ControllerService.cs" />
<Compile Include="Services\AzureDirectoryService.cs" />
<Compile Include="Services\LdapDirectoryService.cs" />
<Compile Include="Services\IDirectoryService.cs" />
<Compile Include="Services\SettingsService.cs" />
<Compile Include="Utilities\AzureAuthenticationProvider.cs" />
<Compile Include="Utilities\Constants.cs" />
<Compile Include="Utilities\Crypto.cs" />
<Compile Include="Services\TokenService.cs" />
<Compile Include="Services\AuthService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utilities\Extensions.cs" />
<Compile Include="Utilities\Helpers.cs" />
<Compile Include="Utilities\Sync.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Enums
{
public enum DirectoryType : byte
{
ActiveDirectory = 0,
AzureActiveDirectory = 1,
Other = 2,
GSuite = 3
}
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Enums
{
public enum OrganizationUserStatusType : byte
{
Invited = 0,
Accepted = 1,
Confirmed = 2
}
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Enums
{
public enum OrganizationUserType : byte
{
Owner = 0,
Admin = 1,
User = 2
}
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Enums
{
[Flags]
public enum UserAccountControl : int
{
AccountDisabled = 0x00000002,
LockOut = 0x00000010,
}
}

View File

@@ -1,83 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
using Bit.Core.Utilities;
namespace Core
{
[RunInstaller(true)]
[DesignerCategory("Code")]
public class Installer : System.Configuration.Install.Installer
{
private IContainer _components = null;
private ServiceProcessInstaller _serviceProcessInstaller;
private ServiceInstaller _serviceInstaller;
public Installer()
{
Init();
}
private void Init()
{
_components = new Container();
_serviceProcessInstaller = new ServiceProcessInstaller();
_serviceInstaller = new ServiceInstaller();
_serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
_serviceProcessInstaller.AfterInstall += new InstallEventHandler(AfterInstalled);
_serviceProcessInstaller.BeforeInstall += new InstallEventHandler(BeforeInstalled);
_serviceInstaller.ServiceName = Constants.ProgramName;
_serviceInstaller.Description = "Sync directory groups and users to your bitwarden organization.";
Installers.AddRange(new System.Configuration.Install.Installer[] { _serviceProcessInstaller, _serviceInstaller });
}
private void AfterInstalled(object sender, InstallEventArgs e)
{
if(!Directory.Exists(Constants.BaseStoragePath))
{
Directory.CreateDirectory(Constants.BaseStoragePath);
}
var info = new DirectoryInfo(Constants.BaseStoragePath);
var sec = info.GetAccessControl();
var adminRule = new FileSystemAccessRule(
new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null),
FileSystemRights.FullControl | FileSystemRights.Write | FileSystemRights.Read,
InheritanceFlags.None,
PropagationFlags.NoPropagateInherit,
AccessControlType.Allow);
sec.AddAccessRule(adminRule);
var userRule = new FileSystemAccessRule(
WindowsIdentity.GetCurrent().Name,
FileSystemRights.FullControl | FileSystemRights.Write | FileSystemRights.Read,
InheritanceFlags.None,
PropagationFlags.NoPropagateInherit,
AccessControlType.Allow);
sec.AddAccessRule(userRule);
sec.SetAccessRuleProtection(isProtected: true, preserveInheritance: false);
info.SetAccessControl(sec);
}
private void BeforeInstalled(object sender, InstallEventArgs e)
{
if(EventLog.SourceExists(_serviceInstaller.ServiceName))
{
EventLog.DeleteEventSource(_serviceInstaller.ServiceName);
}
}
}
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class ApiError
{
public string Message { get; set; }
}
}

View File

@@ -1,75 +0,0 @@
using System.Collections.Generic;
using System.Net;
namespace Bit.Core.Models
{
public class ApiResult<T>
{
private List<ApiError> m_errors = new List<ApiError>();
public bool Succeeded { get; private set; }
public T Result { get; set; }
public IEnumerable<ApiError> Errors => m_errors;
public HttpStatusCode StatusCode { get; private set; }
public static ApiResult<T> Success(T result, HttpStatusCode statusCode)
{
return new ApiResult<T>
{
Succeeded = true,
Result = result,
StatusCode = statusCode
};
}
public static ApiResult<T> Failed(HttpStatusCode statusCode, params ApiError[] errors)
{
var result = new ApiResult<T>
{
Succeeded = false,
StatusCode = statusCode
};
if(errors != null)
{
result.m_errors.AddRange(errors);
}
return result;
}
}
public class ApiResult
{
private List<ApiError> m_errors = new List<ApiError>();
public bool Succeeded { get; private set; }
public IEnumerable<ApiError> Errors => m_errors;
public HttpStatusCode StatusCode { get; private set; }
public static ApiResult Success(HttpStatusCode statusCode)
{
return new ApiResult
{
Succeeded = true,
StatusCode = statusCode
};
}
public static ApiResult Failed(HttpStatusCode statusCode, params ApiError[] errors)
{
var result = new ApiResult
{
Succeeded = false,
StatusCode = statusCode
};
if(errors != null)
{
result.m_errors.AddRange(errors);
}
return result;
}
}
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices;
namespace Bit.Core.Models
{
public class AzureConfiguration
{
public string Tenant { get; set; } = "yourcompany.onmicrosoft.com";
public string Id { get; set; }
public EncryptedData Secret { get; set; }
}
}

View File

@@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class EncryptedData
{
public EncryptedData() { }
public EncryptedData(byte[] plainValue)
{
IV = RandomBytes();
Value = ProtectedData.Protect(plainValue, IV, DataProtectionScope.LocalMachine);
}
public EncryptedData(string plainValue)
{
var bytes = Encoding.UTF8.GetBytes(plainValue);
IV = RandomBytes();
Value = ProtectedData.Protect(bytes, IV, DataProtectionScope.LocalMachine);
}
public byte[] Value { get; set; }
public byte[] IV { get; set; }
public byte[] Decrypt()
{
return ProtectedData.Unprotect(Value, IV, DataProtectionScope.LocalMachine);
}
public string DecryptToString()
{
var bytes = ProtectedData.Unprotect(Value, IV, DataProtectionScope.LocalMachine);
return Encoding.UTF8.GetString(bytes);
}
private byte[] RandomBytes()
{
var entropy = new byte[16];
new RNGCryptoServiceProvider().GetBytes(entropy);
return entropy;
}
}
}

View File

@@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public abstract class Entry
{
public string ReferenceId { get; set; }
public string ExternalId { get; set; }
public DateTime? CreationDate { get; set; }
public DateTime? RevisionDate { get; set; }
}
public class GroupEntry : Entry
{
public string Name { get; set; }
public HashSet<string> UserMemberExternalIds { get; set; } = new HashSet<string>();
public HashSet<string> GroupMemberReferenceIds { get; set; } = new HashSet<string>();
}
public class UserEntry : Entry
{
public string Email { get; set; }
public bool Disabled { get; set; }
public bool Deleted { get; set; }
}
}

View File

@@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class ErrorResponse
{
public string Message { get; set; }
public Dictionary<string, IEnumerable<string>> ValidationErrors { get; set; }
// For use in development environments.
public string ExceptionMessage { get; set; }
public string ExceptionStackTrace { get; set; }
public string InnerExceptionMessage { get; set; }
}
}

View File

@@ -1,10 +0,0 @@
namespace Bit.Core.Models
{
public class GSuiteConfiguration
{
public string SecretFile { get; set; } = "client_secret.json";
public string Customer { get; set; }
public string Domain { get; set; } = "yourcompany.com";
public string AdminUser { get; set; } = "adminuser@yourcompany.com";
}
}

View File

@@ -1,47 +0,0 @@
using Bit.Core.Services;
using System.Collections.Generic;
using System.Linq;
namespace Bit.Core.Models
{
public class ImportRequest
{
public ImportRequest(List<GroupEntry> groups, List<UserEntry> users)
{
Groups = groups?.Select(g => new Group(g)).ToArray() ?? new Group[] { };
Users = users?.Select(u => new User(u)).ToArray() ?? new User[] { };
}
public Group[] Groups { get; set; }
public User[] Users { get; set; }
public class Group
{
public Group(GroupEntry entry)
{
Name = entry.Name;
ExternalId = entry.ExternalId;
Users = entry.UserMemberExternalIds;
}
public string Name { get; set; }
public string ExternalId { get; set; }
public IEnumerable<string> Users { get; set; }
}
public class User
{
public User(UserEntry entry)
{
Email = entry.Email;
Deleted = (SettingsService.Instance.Sync.RemoveDisabledUsers && entry.Disabled) || entry.Deleted;
ExternalId = entry.ExternalId;
}
public string ExternalId { get; set; }
public string Email { get; set; }
public bool Deleted { get; set; }
}
}
}

View File

@@ -1,65 +0,0 @@
using Bit.Core.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class LdapConfiguration
{
public string Address { get; set; }
public string Port { get; set; } = "389";
public string Path { get; set; }
public string Username { get; set; }
public EncryptedData Password { get; set; }
public Enums.DirectoryType Type { get; set; } = Enums.DirectoryType.ActiveDirectory;
public DirectoryEntry GetUserDirectoryEntry()
{
return GetPathedDirectoryEntry(SettingsService.Instance.Sync.Ldap.UserPath);
}
public DirectoryEntry GetGroupDirectoryEntry()
{
return GetPathedDirectoryEntry(SettingsService.Instance.Sync.Ldap.GroupPath);
}
public DirectoryEntry GetPathedDirectoryEntry(string pathPrefix = null)
{
var path = Path;
if(!string.IsNullOrWhiteSpace(pathPrefix))
{
path = string.Concat(pathPrefix, ",", path);
}
return GetDirectoryEntry(path);
}
public DirectoryEntry GetBasePathDirectoryEntry()
{
var path = Path.Substring(Path.IndexOf("dc=", StringComparison.InvariantCultureIgnoreCase));
return GetDirectoryEntry(path);
}
public DirectoryEntry GetDirectoryEntry(string path = null)
{
if(Password == null && string.IsNullOrWhiteSpace(Username))
{
return new DirectoryEntry(ServerPath(path));
}
else
{
return new DirectoryEntry(ServerPath(path), Username, Password.DecryptToString(), AuthenticationTypes.None);
}
}
private string ServerPath(string path)
{
return $"LDAP://{Address}:{Port}/{path}";
}
}
}

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class LoginResult
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
public bool TwoFactorRequired { get; set; }
public string MasterPasswordHash { get; set; }
public List<Organization> Organizations { get; set; }
}
}

View File

@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class Organization
{
public Organization() { }
public Organization(ProfileOrganizationResponseModel org)
{
Name = org.Name;
Id = org.Id;
}
public string Name { get; set; }
public string Id { get; set; }
}
}

View File

@@ -1,20 +0,0 @@
using Bit.Core.Enums;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class ProfileOrganizationResponseModel
{
public string Id { get; set; }
public string Name { get; set; }
public string Key { get; set; }
public OrganizationUserStatusType Status { get; set; }
public OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
}
}

View File

@@ -1,20 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class ProfileResponse
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string MasterPasswordHint { get; set; }
public string Culture { get; set; }
public bool TwoFactorEnabled { get; set; }
public IEnumerable<ProfileOrganizationResponseModel> Organizations { get; set; }
}
}

View File

@@ -1,18 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class ServerConfiguration
{
public Enums.DirectoryType Type { get; set; } = Enums.DirectoryType.ActiveDirectory;
public LdapConfiguration Ldap { get; set; }
public AzureConfiguration Azure { get; set; }
public GSuiteConfiguration GSuite { get; set; }
}
}

View File

@@ -1,109 +0,0 @@
using Bit.Core.Enums;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class SyncConfiguration
{
public SyncConfiguration() { }
public SyncConfiguration(DirectoryType type)
{
Ldap = new LdapSyncConfiguration(type);
switch(type)
{
case DirectoryType.ActiveDirectory:
break;
case DirectoryType.AzureActiveDirectory:
break;
case DirectoryType.Other:
break;
case DirectoryType.GSuite:
break;
default:
break;
}
}
/*
* Depending on what server type you are using, filters are be one of the following:
*
* 1. ActiveDirectory or Other
* - LDAP query/filter syntax
* - Read more at: http://bit.ly/2qyLpzW
* - ex. "(&(givenName=John)(|(l=Dallas)(l=Austin)))"
*
* 2. AzureActiveDirectory
* - OData syntax for a Microsoft Graph query parameter '$filter'
* - Read more at http://bit.ly/2q3FOOD
* - ex. "startswith(displayName,'J')"
*
* 3. GSuite
* - Group Filter
* - Custom filtering syntax that allows you to exclude or include a comma separated list of group names.
* - ex. "include:Group A,Sales People,My Other Group"
* or "exclude:Group C,Developers,Some Other Group"
* - User Filter
* - Custom filtering syntax that allows you to exclude or include a comma separated list of group names.
* - Allows you to concatenate a GSuite Admin API user search query to the end of the filter after delimiting
* the include/exclude filter with a pipe (|).
* - Read more at http://bit.ly/2rlTskX
* - ex.
* or "include:joe@company.com,bill@company.com,tom@company.com"
* or "exclude:john@company.com,bill@company.com|orgName=Engineering orgTitle:Manager"
* or "|orgName=Engineering orgTitle:Manager"
*/
public string GroupFilter { get; set; }
public string UserFilter { get; set; }
public bool SyncGroups { get; set; } = true;
public bool SyncUsers { get; set; } = true;
public int IntervalMinutes { get; set; } = 5;
public bool RemoveDisabledUsers { get; set; }
public LdapSyncConfiguration Ldap { get; set; } = new LdapSyncConfiguration();
public class LdapSyncConfiguration
{
public LdapSyncConfiguration() { }
public LdapSyncConfiguration(DirectoryType type)
{
switch(type)
{
case DirectoryType.ActiveDirectory:
CreationDateAttribute = "whenCreated";
RevisionDateAttribute = "whenChanged";
UserEmailPrefixAttribute = "sAMAccountName";
UserPath = "CN=Users";
GroupPath = "CN=Users";
break;
case DirectoryType.Other:
break;
default:
break;
}
}
public string UserPath { get; set; }
public string GroupPath { get; set; }
public string UserObjectClass { get; set; } = "person";
public string GroupObjectClass { get; set; } = "group";
public string MemberAttribute { get; set; } = "member";
public string GroupNameAttribute { get; set; } = "name";
public string UserEmailAttribute { get; set; } = "mail";
public bool EmailPrefixSuffix { get; set; } = false;
public string UserEmailPrefixAttribute { get; set; } = "cn";
public string UserEmailSuffix { get; set; } = "@companyname.com";
public string CreationDateAttribute { get; set; }
public string RevisionDateAttribute { get; set; }
}
}
}

View File

@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class SyncResult
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
public List<GroupEntry> Groups { get; set; } = new List<GroupEntry>();
public List<UserEntry> Users { get; set; } = new List<UserEntry>();
}
}

View File

@@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
namespace Bit.Core.Models
{
public class TokenRequest
{
public string Email { get; set; }
public string MasterPasswordHash { get; set; }
public string Token { get; set; }
public int? Provider { get; set; }
public IDictionary<string, string> ToIdentityTokenRequest()
{
var dict = new Dictionary<string, string>
{
{ "grant_type", "password" },
{ "username", Email },
{ "password", MasterPasswordHash },
{ "scope", "api offline_access" },
{ "client_id", "connector" }
};
if(Token != null && Provider.HasValue)
{
dict.Add("TwoFactorToken", Token);
dict.Add("TwoFactorProvider", Provider.Value.ToString());
}
return dict;
}
}
}

View File

@@ -1,23 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Models
{
public class TokenResponse
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("expires_in")]
public long ExpiresIn { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
public List<int> TwoFactorProviders { get; set; }
public string PrivateKey { get; set; }
}
}

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Core")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Core")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ae082484-a34c-4b3a-a69f-49e5ef298b27")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,293 +0,0 @@
using Bit.Core.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Services
{
public class ApiService
{
private static ApiService _instance;
private ApiService()
{
Client = new HttpClient();
}
public static ApiService Instance
{
get
{
if(_instance == null)
{
_instance = new ApiService();
}
return _instance;
}
}
protected HttpClient Client { get; private set; }
public virtual async Task<ApiResult<TokenResponse>> PostTokenAsync(TokenRequest requestObj)
{
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(string.Concat(SettingsService.Instance.IdentityBaseUrl, "/connect/token")),
Content = new FormUrlEncodedContent(requestObj.ToIdentityTokenRequest())
};
try
{
var response = await Client.SendAsync(requestMessage).ConfigureAwait(false);
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if(!response.IsSuccessStatusCode)
{
var errorResponse = JObject.Parse(responseContent);
if(errorResponse["TwoFactorProviders"] != null)
{
return ApiResult<TokenResponse>.Success(new TokenResponse
{
TwoFactorProviders = errorResponse["TwoFactorProviders"].ToObject<List<int>>()
}, response.StatusCode);
}
return await HandleErrorAsync<TokenResponse>(response).ConfigureAwait(false);
}
var responseObj = JsonConvert.DeserializeObject<TokenResponse>(responseContent);
return ApiResult<TokenResponse>.Success(responseObj, response.StatusCode);
}
catch
{
return HandledWebException<TokenResponse>();
}
}
public virtual async Task<ApiResult> PostImportAsync(ImportRequest requestObj)
{
var tokenStateResponse = await HandleTokenStateAsync();
if(!tokenStateResponse.Succeeded)
{
return tokenStateResponse;
}
var stringContent = JsonConvert.SerializeObject(requestObj);
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(string.Concat(SettingsService.Instance.ApiBaseUrl, "/organizations/",
SettingsService.Instance.Organization.Id, "/import")),
Content = new StringContent(stringContent, Encoding.UTF8, "application/json"),
};
requestMessage.Headers.Add("Authorization", $"Bearer3 {TokenService.Instance.AccessToken}");
try
{
var response = await Client.SendAsync(requestMessage).ConfigureAwait(false);
if(!response.IsSuccessStatusCode)
{
return await HandleErrorAsync(response).ConfigureAwait(false);
}
return ApiResult.Success(response.StatusCode);
}
catch
{
return HandledWebException();
}
}
public virtual async Task<ApiResult<ProfileResponse>> GetProfileAsync()
{
var tokenStateResponse = await HandleTokenStateAsync<ProfileResponse>();
if(!tokenStateResponse.Succeeded)
{
return tokenStateResponse;
}
var requestMessage = new HttpRequestMessage()
{
Method = HttpMethod.Get,
RequestUri = new Uri(string.Concat(SettingsService.Instance.ApiBaseUrl, "/accounts/profile")),
};
requestMessage.Headers.Add("Authorization", $"Bearer3 {TokenService.Instance.AccessToken}");
try
{
var response = await Client.SendAsync(requestMessage).ConfigureAwait(false);
if(!response.IsSuccessStatusCode)
{
return await HandleErrorAsync<ProfileResponse>(response).ConfigureAwait(false);
}
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var responseObj = JsonConvert.DeserializeObject<ProfileResponse>(responseContent);
return ApiResult<ProfileResponse>.Success(responseObj, response.StatusCode);
}
catch
{
return HandledWebException<ProfileResponse>();
}
}
protected ApiResult HandledWebException()
{
return ApiResult.Failed(HttpStatusCode.BadGateway,
new ApiError { Message = "There is a problem connecting to the server." });
}
protected ApiResult<T> HandledWebException<T>()
{
return ApiResult<T>.Failed(HttpStatusCode.BadGateway,
new ApiError { Message = "There is a problem connecting to the server." });
}
protected async Task<ApiResult> HandleTokenStateAsync()
{
return await HandleTokenStateAsync(
() => ApiResult.Success(HttpStatusCode.OK),
() => HandledWebException(),
(r) => HandleErrorAsync(r));
}
protected async Task<ApiResult<T>> HandleTokenStateAsync<T>()
{
return await HandleTokenStateAsync(
() => ApiResult<T>.Success(default(T), HttpStatusCode.OK),
() => HandledWebException<T>(),
(r) => HandleErrorAsync<T>(r));
}
private async Task<T> HandleTokenStateAsync<T>(Func<T> success, Func<T> webException,
Func<HttpResponseMessage, Task<T>> error)
{
if(TokenService.Instance.AccessTokenNeedsRefresh && !string.IsNullOrWhiteSpace(TokenService.Instance.RefreshToken))
{
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(string.Concat(SettingsService.Instance.IdentityBaseUrl, "/connect/token")),
Content = new FormUrlEncodedContent(
new Dictionary<string, string>
{
{ "grant_type", "refresh_token" },
{ "client_id", "mobile" },
{ "refresh_token", TokenService.Instance.RefreshToken }
})
};
try
{
var response = await Client.SendAsync(requestMessage).ConfigureAwait(false);
if(!response.IsSuccessStatusCode)
{
if(response.StatusCode == HttpStatusCode.BadRequest)
{
response.StatusCode = HttpStatusCode.Unauthorized;
}
return await error.Invoke(response).ConfigureAwait(false);
}
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(responseContent);
TokenService.Instance.AccessToken = tokenResponse.AccessToken;
TokenService.Instance.RefreshToken = tokenResponse.RefreshToken;
}
catch
{
return webException.Invoke();
}
}
return success.Invoke();
}
protected async Task<ApiResult<T>> HandleErrorAsync<T>(HttpResponseMessage response)
{
try
{
var errors = await ParseErrorsAsync(response).ConfigureAwait(false);
return ApiResult<T>.Failed(response.StatusCode, errors.ToArray());
}
catch
{ }
return ApiResult<T>.Failed(response.StatusCode,
new ApiError { Message = "An unknown error has occurred." });
}
protected async Task<ApiResult> HandleErrorAsync(HttpResponseMessage response)
{
try
{
var errors = await ParseErrorsAsync(response).ConfigureAwait(false);
return ApiResult.Failed(response.StatusCode, errors.ToArray());
}
catch
{ }
return ApiResult.Failed(response.StatusCode,
new ApiError { Message = "An unknown error has occurred." });
}
private async Task<List<ApiError>> ParseErrorsAsync(HttpResponseMessage response)
{
var errors = new List<ApiError>();
var statusCode = (int)response.StatusCode;
if(statusCode >= 400 && statusCode <= 500)
{
ErrorResponse errorResponseModel = null;
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if(!string.IsNullOrWhiteSpace(responseContent))
{
var errorResponse = JObject.Parse(responseContent);
if(errorResponse["ErrorModel"] != null && errorResponse["ErrorModel"]["Message"] != null)
{
errorResponseModel = errorResponse["ErrorModel"].ToObject<ErrorResponse>();
}
else if(errorResponse["Message"] != null)
{
errorResponseModel = errorResponse.ToObject<ErrorResponse>();
}
}
if(errorResponseModel != null)
{
if((errorResponseModel.ValidationErrors?.Count ?? 0) > 0)
{
foreach(var valError in errorResponseModel.ValidationErrors)
{
foreach(var errorMessage in valError.Value)
{
errors.Add(new ApiError { Message = errorMessage });
}
}
}
else
{
errors.Add(new ApiError { Message = errorResponseModel.Message });
}
}
}
if(errors.Count == 0)
{
errors.Add(new ApiError { Message = "An unknown error has occurred." });
}
return errors;
}
}
}

View File

@@ -1,151 +0,0 @@
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Services
{
public class AuthService
{
private static AuthService _instance;
private AuthService() { }
public static AuthService Instance
{
get
{
if(_instance == null)
{
_instance = new AuthService();
}
return _instance;
}
}
public bool Authenticated => !string.IsNullOrWhiteSpace(TokenService.Instance.AccessToken);
public bool OrganizationSet => SettingsService.Instance.Organization != null;
public void LogOut()
{
TokenService.Instance.AccessToken = null;
TokenService.Instance.RefreshToken = null;
}
public async Task<LoginResult> LogInAsync(string email, string masterPassword)
{
var normalizedEmail = email.Trim().ToLower();
var key = Crypto.MakeKeyFromPassword(masterPassword, normalizedEmail);
var request = new TokenRequest
{
Email = normalizedEmail,
MasterPasswordHash = Crypto.HashPasswordBase64(key, masterPassword)
};
var response = await ApiService.Instance.PostTokenAsync(request);
masterPassword = null;
key = null;
var result = new LoginResult();
if(!response.Succeeded)
{
result.Success = false;
result.ErrorMessage = response.Errors.FirstOrDefault()?.Message;
return result;
}
result.Success = true;
if(response.Result.TwoFactorProviders != null && response.Result.TwoFactorProviders.Count > 0)
{
result.TwoFactorRequired = true;
result.MasterPasswordHash = request.MasterPasswordHash;
return result;
}
return await ProcessLogInSuccessAsync(response.Result);
}
public async Task<LoginResult> LogInTwoFactorAsync(string token, string email, string masterPassword)
{
var normalizedEmail = email.Trim().ToLower();
var key = Crypto.MakeKeyFromPassword(masterPassword, normalizedEmail);
var result = await LogInTwoFactorWithHashAsync(token, email, Crypto.HashPasswordBase64(key, masterPassword));
key = null;
masterPassword = null;
return result;
}
public async Task<LoginResult> LogInTwoFactorWithHashAsync(string token, string email, string masterPasswordHash)
{
var request = new TokenRequest
{
Email = email.Trim().ToLower(),
MasterPasswordHash = masterPasswordHash,
Token = token.Trim().Replace(" ", ""),
Provider = 0 // Authenticator app (only 1 provider for now, so hard coded)
};
var response = await ApiService.Instance.PostTokenAsync(request);
if(!response.Succeeded)
{
var result = new LoginResult();
result.Success = false;
result.ErrorMessage = response.Errors.FirstOrDefault()?.Message;
return result;
}
return await ProcessLogInSuccessAsync(response.Result);
}
private async Task<LoginResult> ProcessLogInSuccessAsync(TokenResponse response)
{
TokenService.Instance.AccessToken = response.AccessToken;
TokenService.Instance.RefreshToken = response.RefreshToken;
var result = new LoginResult();
var profile = await ApiService.Instance.GetProfileAsync();
if(profile.Succeeded)
{
var adminOrgs = profile.Result.Organizations.Where(o =>
o.Status == OrganizationUserStatusType.Confirmed &&
o.Type != OrganizationUserType.User);
if(!adminOrgs.Any())
{
LogOut();
result.Success = false;
result.ErrorMessage = "You are not an admin of any organizations.";
return result;
}
result.Organizations = adminOrgs.Select(o => new Organization(o)).ToList();
if(result.Organizations.Count == 1)
{
SettingsService.Instance.Organization = new Organization(adminOrgs.First());
}
result.Success = true;
return result;
}
else
{
LogOut();
result.Success = false;
result.ErrorMessage = "Could not load profile.";
return result;
}
}
}
}

View File

@@ -1,284 +0,0 @@
using Bit.Core.Models;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Graph;
using System.Net.Http.Headers;
using System.Diagnostics;
using System.Linq;
using Bit.Core.Utilities;
namespace Bit.Core.Services
{
public class AzureDirectoryService : IDirectoryService
{
private static AzureDirectoryService _instance;
private static GraphServiceClient _graphClient;
private AzureDirectoryService()
{
_graphClient = new GraphServiceClient(new AzureAuthenticationProvider());
}
public static IDirectoryService Instance
{
get
{
if(_instance == null)
{
_instance = new AzureDirectoryService();
}
return _instance;
}
}
public async Task<Tuple<List<GroupEntry>, List<UserEntry>>> GetEntriesAsync(bool force = false)
{
if(!AuthService.Instance.Authenticated || !AuthService.Instance.OrganizationSet)
{
throw new ApplicationException("Not logged in or have an org set.");
}
if(SettingsService.Instance.Server?.Azure == null)
{
throw new ApplicationException("No configuration for directory server.");
}
if(SettingsService.Instance.Sync == null)
{
throw new ApplicationException("No configuration for sync.");
}
List<UserEntry> users = null;
if(SettingsService.Instance.Sync.SyncUsers)
{
users = await GetUsersAsync(force);
}
List<GroupEntry> groups = null;
if(SettingsService.Instance.Sync.SyncGroups)
{
groups = await GetGroupsAsync(force || (users?.Any(u => !u.Deleted && !u.Disabled) ?? false));
}
return new Tuple<List<GroupEntry>, List<UserEntry>>(groups, users);
}
private async static Task<List<GroupEntry>> GetGroupsAsync(bool force = false)
{
if(!SettingsService.Instance.Sync.SyncGroups)
{
throw new ApplicationException("Not configured to sync groups.");
}
if(SettingsService.Instance.Server?.Azure == null)
{
throw new ApplicationException("No configuration for directory server.");
}
if(SettingsService.Instance.Sync == null)
{
throw new ApplicationException("No configuration for sync.");
}
if(!AuthService.Instance.Authenticated)
{
throw new ApplicationException("Not authenticated.");
}
var entries = new List<GroupEntry>();
var changedGroupIds = new List<string>();
var getFullResults = SettingsService.Instance.GroupDeltaToken == null || force;
try
{
var delataRequest = _graphClient.Groups.Delta().Request().Filter(SettingsService.Instance.Sync.GroupFilter);
if(!getFullResults)
{
delataRequest.QueryOptions.Add(new QueryOption("$deltatoken", SettingsService.Instance.GroupDeltaToken));
}
var groupsDelta = await delataRequest.GetAsync();
while(true)
{
if(getFullResults)
{
foreach(var group in groupsDelta)
{
var entry = await BuildGroupAsync(group);
entries.Add(entry);
}
}
else
{
changedGroupIds.AddRange(groupsDelta.Select(g => g.Id));
}
if(groupsDelta.NextPageRequest == null)
{
object deltaLink;
if(groupsDelta.AdditionalData.TryGetValue("@odata.deltaLink", out deltaLink))
{
var deltaUriQuery = new Uri(deltaLink.ToString()).ParseQueryString();
if(deltaUriQuery["$deltatoken"] != null)
{
SettingsService.Instance.GroupDeltaToken = deltaUriQuery["$deltatoken"];
}
}
break;
}
else
{
groupsDelta = await groupsDelta.NextPageRequest.GetAsync();
}
}
}
catch { }
if(getFullResults || (!getFullResults && !changedGroupIds.Any()))
{
return entries;
}
var groups = await _graphClient.Groups.Request().Filter(SettingsService.Instance.Sync.GroupFilter).GetAsync();
while(true)
{
foreach(var group in groups)
{
var entry = await BuildGroupAsync(group);
entries.Add(entry);
}
if(groups.NextPageRequest == null)
{
break;
}
else
{
groups = await groups.NextPageRequest.GetAsync();
}
}
return entries;
}
private async static Task<GroupEntry> BuildGroupAsync(Group group)
{
var entry = new GroupEntry
{
ReferenceId = group.Id,
ExternalId = group.Id,
Name = group.DisplayName
};
var members = await _graphClient.Groups[group.Id].Members.Request().Select("id").GetAsync();
foreach(var member in members)
{
if(member is User)
{
entry.UserMemberExternalIds.Add(member.Id);
}
else if(member is Group)
{
entry.GroupMemberReferenceIds.Add(member.Id);
}
}
return entry;
}
private async static Task<List<UserEntry>> GetUsersAsync(bool force = false)
{
if(!SettingsService.Instance.Sync.SyncUsers)
{
throw new ApplicationException("Not configured to sync users.");
}
if(SettingsService.Instance.Server?.Azure == null)
{
throw new ApplicationException("No configuration for directory server.");
}
if(SettingsService.Instance.Sync == null)
{
throw new ApplicationException("No configuration for sync.");
}
if(!AuthService.Instance.Authenticated)
{
throw new ApplicationException("Not authenticated.");
}
var entries = new List<UserEntry>();
var userRequest = _graphClient.Users.Delta();
IUserDeltaCollectionPage users = null;
if(!force && SettingsService.Instance.UserDeltaToken != null)
{
try
{
var delataRequest = userRequest.Request().Filter(SettingsService.Instance.Sync.UserFilter);
delataRequest.QueryOptions.Add(new QueryOption("$deltatoken", SettingsService.Instance.UserDeltaToken));
users = await delataRequest.GetAsync();
}
catch
{
users = null;
}
}
if(users == null)
{
users = await userRequest.Request().Filter(SettingsService.Instance.Sync.UserFilter).GetAsync();
}
while(true)
{
foreach(var user in users)
{
var entry = new UserEntry
{
ReferenceId = user.Id,
ExternalId = user.Id,
Email = user.Mail ?? user.UserPrincipalName,
Disabled = !user.AccountEnabled.GetValueOrDefault(true)
};
object deleted;
if(user.AdditionalData.TryGetValue("@removed", out deleted) && deleted.ToString().Contains("changed"))
{
entry.Deleted = true;
}
else if(!entry.Disabled && (entry?.Email?.Contains("#") ?? true))
{
continue;
}
entries.Add(entry);
}
if(users.NextPageRequest == null)
{
object deltaLink;
if(users.AdditionalData.TryGetValue("@odata.deltaLink", out deltaLink))
{
var deltaUriQuery = new Uri(deltaLink.ToString()).ParseQueryString();
if(deltaUriQuery["$deltatoken"] != null)
{
SettingsService.Instance.UserDeltaToken = deltaUriQuery["$deltatoken"];
}
}
break;
}
else
{
users = await users.NextPageRequest.GetAsync();
}
}
return entries;
}
}
}

View File

@@ -1,77 +0,0 @@
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Services
{
public class ControllerService
{
private static ControllerService _instance;
private ControllerService()
{
Controller = new ServiceController(Constants.ProgramName);
}
public static ControllerService Instance
{
get
{
if(_instance == null)
{
_instance = new ControllerService();
}
return _instance;
}
}
public ServiceController Controller { get; private set; }
public ServiceControllerStatus Status
{
get
{
Controller.Refresh();
return Controller.Status;
}
}
public string StatusString => Controller == null ? "Unavailable" : Status.ToString();
public bool Running => Status == ServiceControllerStatus.Running;
public bool Paused => Status == ServiceControllerStatus.Paused;
public bool Stopped => Status == ServiceControllerStatus.Stopped;
public bool Pending =>
Status == ServiceControllerStatus.ContinuePending ||
Status == ServiceControllerStatus.PausePending ||
Status == ServiceControllerStatus.StartPending ||
Status == ServiceControllerStatus.StopPending;
public bool Start()
{
if(Controller == null || !Stopped)
{
return false;
}
Controller.Start();
return true;
}
public bool Stop()
{
if(Controller == null || !Controller.CanStop)
{
return false;
}
Controller.Stop();
return true;
}
}
}

View File

@@ -1,324 +0,0 @@
using Bit.Core.Models;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Google.Apis.Admin.Directory.directory_v1;
using Google.Apis.Services;
using Google.Apis.Auth.OAuth2;
using System.IO;
using Bit.Core.Utilities;
using System.Linq;
using Google.Apis.Admin.Directory.directory_v1.Data;
using Google.Apis.Requests;
using Google.Apis.Json;
using System.Net;
namespace Bit.Core.Services
{
public class GSuiteDirectoryService : IDirectoryService
{
private static GSuiteDirectoryService _instance;
private static DirectoryService _service;
private GSuiteDirectoryService()
{
ICredential creds;
var secretFilePath = Path.Combine(Constants.BaseStoragePath, SettingsService.Instance.Server.GSuite.SecretFile);
using(var stream = new FileStream(secretFilePath, FileMode.Open, FileAccess.Read))
{
//creds = GoogleCredential.FromStream(stream).CreateScoped(scopes);
var credParams = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(stream);
creds = CreateServiceAccountCredential(credParams);
}
_service = new DirectoryService(new BaseClientService.Initializer
{
HttpClientInitializer = creds,
ApplicationName = Constants.ProgramName
});
}
public static IDirectoryService Instance
{
get
{
if(_instance == null)
{
_instance = new GSuiteDirectoryService();
}
return _instance;
}
}
public async Task<Tuple<List<GroupEntry>, List<UserEntry>>> GetEntriesAsync(bool force = false)
{
if(!AuthService.Instance.Authenticated || !AuthService.Instance.OrganizationSet)
{
throw new ApplicationException("Not logged in or have an org set.");
}
if(SettingsService.Instance.Server?.GSuite == null)
{
throw new ApplicationException("No configuration for directory server.");
}
if(SettingsService.Instance.Sync == null)
{
throw new ApplicationException("No configuration for sync.");
}
List<UserEntry> users = null;
if(SettingsService.Instance.Sync.SyncUsers)
{
users = await GetUsersAsync(force);
}
List<GroupEntry> groups = null;
if(SettingsService.Instance.Sync.SyncGroups)
{
groups = await GetGroupsAsync(force || (users?.Any(u => !u.Deleted && !u.Disabled) ?? false));
}
return new Tuple<List<GroupEntry>, List<UserEntry>>(groups, users);
}
private Task<List<GroupEntry>> GetGroupsAsync(bool force)
{
var entries = new List<GroupEntry>();
var request = _service.Groups.List();
request.Domain = SettingsService.Instance.Server.GSuite.Domain;
request.Customer = SettingsService.Instance.Server.GSuite.Customer;
var pageStreamer = new PageStreamer<Group, GroupsResource.ListRequest, Groups, string>(
(req, token) => req.PageToken = token,
res => res.NextPageToken,
res => res.GroupsValue);
var filter = CreateSetFromFilter(SettingsService.Instance.Sync.GroupFilter);
foreach(var group in pageStreamer.Fetch(request))
{
if(FilterOutResult(filter, group.Name))
{
continue;
}
var entry = BuildGroup(group);
entries.Add(entry);
}
return Task.FromResult(entries);
}
private static GroupEntry BuildGroup(Group group)
{
var entry = new GroupEntry
{
ReferenceId = group.Id,
ExternalId = group.Id,
Name = group.Name
};
var memberRequest = _service.Members.List(group.Id);
var pageStreamer = new PageStreamer<Member, MembersResource.ListRequest, Members, string>(
(req, token) => req.PageToken = token,
res => res.NextPageToken,
res => res.MembersValue);
foreach(var member in pageStreamer.Fetch(memberRequest))
{
if(!member.Role.Equals("member", StringComparison.InvariantCultureIgnoreCase) ||
!member.Status.Equals("active", StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
if(member.Type.Equals("user", StringComparison.InvariantCultureIgnoreCase))
{
entry.UserMemberExternalIds.Add(member.Id);
}
else if(member.Type.Equals("group", StringComparison.InvariantCultureIgnoreCase))
{
entry.GroupMemberReferenceIds.Add(member.Id);
}
}
return entry;
}
private Task<List<UserEntry>> GetUsersAsync(bool force)
{
var entries = new List<UserEntry>();
var query = CreateGSuiteQueryFromFilter(SettingsService.Instance.Sync.UserFilter);
var request = _service.Users.List();
request.Domain = SettingsService.Instance.Server.GSuite.Domain;
request.Customer = SettingsService.Instance.Server.GSuite.Customer;
request.Query = query;
var pageStreamer = new PageStreamer<User, UsersResource.ListRequest, Users, string>(
(req, token) => req.PageToken = token,
res => res.NextPageToken,
res => res.UsersValue);
var filter = CreateSetFromFilter(SettingsService.Instance.Sync.UserFilter);
foreach(var user in pageStreamer.Fetch(request))
{
if(FilterOutResult(filter, user.PrimaryEmail))
{
continue;
}
var entry = BuildUser(user, false);
if(entry != null)
{
entries.Add(entry);
}
}
var deletedRequest = _service.Users.List();
deletedRequest.Domain = SettingsService.Instance.Server.GSuite.Domain;
deletedRequest.Customer = SettingsService.Instance.Server.GSuite.Customer;
deletedRequest.Query = query;
deletedRequest.ShowDeleted = "true";
var deletedPageStreamer = new PageStreamer<User, UsersResource.ListRequest, Users, string>(
(req, token) => req.PageToken = token,
res => res.NextPageToken,
res => res.UsersValue);
foreach(var user in deletedPageStreamer.Fetch(deletedRequest))
{
if(FilterOutResult(filter, user.PrimaryEmail))
{
continue;
}
var entry = BuildUser(user, true);
if(entry != null)
{
entries.Add(entry);
}
}
return Task.FromResult(entries);
}
private UserEntry BuildUser(User user, bool deleted)
{
var entry = new UserEntry
{
ReferenceId = user.Id,
ExternalId = user.Id,
Email = user.PrimaryEmail,
Disabled = user.Suspended.GetValueOrDefault(false),
Deleted = deleted,
CreationDate = user.CreationTime
};
if(string.IsNullOrWhiteSpace(entry.Email) && !entry.Deleted)
{
return null;
}
return entry;
}
private string CreateGSuiteQueryFromFilter(string filter)
{
if(string.IsNullOrWhiteSpace(filter))
{
return null;
}
var mainParts = filter.Split('|');
if(mainParts.Count() < 2 || string.IsNullOrWhiteSpace(mainParts[1]))
{
return null;
}
return mainParts[1].Trim();
}
private Tuple<bool, HashSet<string>> CreateSetFromFilter(string filter)
{
if(string.IsNullOrWhiteSpace(filter))
{
return null;
}
var mainParts = filter.Split('|');
if(mainParts.Count() < 1 || string.IsNullOrWhiteSpace(mainParts[0]))
{
return null;
}
var parts = mainParts[0].Split(':');
if(parts.Count() != 2)
{
return null;
}
var exclude = true;
if(string.Equals(parts[0].Trim(), "include", StringComparison.InvariantCultureIgnoreCase))
{
exclude = false;
}
else if(string.Equals(parts[0].Trim(), "exclude", StringComparison.InvariantCultureIgnoreCase))
{
exclude = true;
}
else
{
return null;
}
var list = new HashSet<string>(parts[1].Split(',').Select(p => p.Trim()));
return new Tuple<bool, HashSet<string>>(exclude, list);
}
private bool FilterOutResult(Tuple<bool, HashSet<string>> filter, string result)
{
if(filter != null)
{
// excluded
if(filter.Item1 && filter.Item2.Contains(result, StringComparer.InvariantCultureIgnoreCase))
{
return true;
}
// included
else if(!filter.Item1 && !filter.Item2.Contains(result, StringComparer.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
private ServiceAccountCredential CreateServiceAccountCredential(JsonCredentialParameters credParams)
{
var scopes = new List<string>
{
DirectoryService.Scope.AdminDirectoryUserReadonly,
DirectoryService.Scope.AdminDirectoryGroupReadonly,
DirectoryService.Scope.AdminDirectoryGroupMemberReadonly
};
if(credParams.Type != JsonCredentialParameters.ServiceAccountCredentialType ||
string.IsNullOrEmpty(credParams.ClientEmail) ||
string.IsNullOrEmpty(credParams.PrivateKey))
{
throw new InvalidOperationException("JSON data does not represent a valid service account credential.");
}
var initializer = new ServiceAccountCredential.Initializer(credParams.ClientEmail);
initializer.User = SettingsService.Instance.Server.GSuite.AdminUser;
initializer.Scopes = scopes;
return new ServiceAccountCredential(initializer.FromPrivateKey(credParams.PrivateKey));
}
}
}

View File

@@ -1,14 +0,0 @@
using Bit.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Services
{
public interface IDirectoryService
{
Task<Tuple<List<GroupEntry>, List<UserEntry>>> GetEntriesAsync(bool force = false);
}
}

View File

@@ -1,397 +0,0 @@
using Bit.Core.Enums;
using Bit.Core.Models;
using Bit.Core.Utilities;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Bit.Core.Services
{
public class LdapDirectoryService : IDirectoryService
{
private static LdapDirectoryService _instance;
private LdapDirectoryService() { }
public static IDirectoryService Instance
{
get
{
if(_instance == null)
{
_instance = new LdapDirectoryService();
}
return _instance;
}
}
public async Task<Tuple<List<GroupEntry>, List<UserEntry>>> GetEntriesAsync(bool force = false)
{
if(!AuthService.Instance.Authenticated || !AuthService.Instance.OrganizationSet)
{
throw new ApplicationException("Not logged in or have an org set.");
}
if(SettingsService.Instance.Server?.Ldap == null)
{
throw new ApplicationException("No configuration for directory server.");
}
if(SettingsService.Instance.Sync == null)
{
throw new ApplicationException("No configuration for sync.");
}
List<UserEntry> users = null;
if(SettingsService.Instance.Sync.SyncUsers)
{
users = await GetUsersAsync(force);
}
List<GroupEntry> groups = null;
if(SettingsService.Instance.Sync.SyncGroups)
{
groups = await GetGroupsAsync(force || (users?.Any(u => !u.Deleted && !u.Disabled) ?? false));
}
return new Tuple<List<GroupEntry>, List<UserEntry>>(groups, users);
}
private static Task<List<GroupEntry>> GetGroupsAsync(bool force = false)
{
if(!SettingsService.Instance.Sync.SyncGroups)
{
throw new ApplicationException("Not configured to sync groups.");
}
if(SettingsService.Instance.Server?.Ldap == null)
{
throw new ApplicationException("No configuration for directory server.");
}
if(SettingsService.Instance.Sync == null)
{
throw new ApplicationException("No configuration for sync.");
}
if(!AuthService.Instance.Authenticated)
{
throw new ApplicationException("Not authenticated.");
}
var entry = SettingsService.Instance.Server.Ldap.GetGroupDirectoryEntry();
var originalFilter = BuildBaseFilter(SettingsService.Instance.Sync.Ldap.GroupObjectClass,
SettingsService.Instance.Sync.GroupFilter);
var filter = originalFilter;
var revisionFilter = BuildRevisionFilter(filter, force, SettingsService.Instance.LastGroupSyncDate);
var searchSinceRevision = filter != revisionFilter;
filter = revisionFilter;
var searcher = new DirectorySearcher(entry, filter);
var result = searcher.FindAll();
var initialSearchGroupIds = new List<string>();
foreach(SearchResult item in result)
{
initialSearchGroupIds.Add(DNFromPath(item.Path));
}
if(searchSinceRevision && !initialSearchGroupIds.Any())
{
return Task.FromResult(new List<GroupEntry>());
}
else if(searchSinceRevision)
{
searcher = new DirectorySearcher(entry, originalFilter);
result = searcher.FindAll();
}
var userFilter = BuildBaseFilter(SettingsService.Instance.Sync.Ldap.UserObjectClass,
SettingsService.Instance.Sync.UserFilter);
var userSearcher = new DirectorySearcher(entry, userFilter);
var userResult = userSearcher.FindAll();
var userIdsDict = MakeIdIndex(userResult);
var groups = new List<GroupEntry>();
foreach(SearchResult item in result)
{
var group = BuildGroup(item, userIdsDict);
if(group == null)
{
continue;
}
groups.Add(group);
}
return Task.FromResult(groups);
}
private static Dictionary<string, string> MakeIdIndex(SearchResultCollection result)
{
var dict = new Dictionary<string, string>();
foreach(SearchResult item in result)
{
var referenceId = DNFromPath(item.Path);
var externalId = referenceId;
if(item.Properties.Contains("objectGUID") && item.Properties["objectGUID"].Count > 0)
{
externalId = item.Properties["objectGUID"][0].FromGuidToString();
}
dict.Add(referenceId, externalId);
}
return dict;
}
private static GroupEntry BuildGroup(SearchResult item, Dictionary<string, string> userIndex)
{
var group = new GroupEntry
{
ReferenceId = DNFromPath(item.Path)
};
if(group.ReferenceId == null)
{
return null;
}
// External Id
if(item.Properties.Contains("objectGUID") && item.Properties["objectGUID"].Count > 0)
{
group.ExternalId = item.Properties["objectGUID"][0].FromGuidToString();
}
else
{
group.ExternalId = group.ReferenceId;
}
// Name
if(item.Properties.Contains(SettingsService.Instance.Sync.Ldap.GroupNameAttribute) &&
item.Properties[SettingsService.Instance.Sync.Ldap.GroupNameAttribute].Count > 0)
{
group.Name = item.Properties[SettingsService.Instance.Sync.Ldap.GroupNameAttribute][0].ToString();
}
else if(item.Properties.Contains("cn") && item.Properties["cn"].Count > 0)
{
group.Name = item.Properties["cn"][0].ToString();
}
else
{
return null;
}
// Dates
group.CreationDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.Ldap.CreationDateAttribute);
group.RevisionDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.Ldap.RevisionDateAttribute);
// Members
if(item.Properties.Contains(SettingsService.Instance.Sync.Ldap.MemberAttribute) &&
item.Properties[SettingsService.Instance.Sync.Ldap.MemberAttribute].Count > 0)
{
foreach(var member in item.Properties[SettingsService.Instance.Sync.Ldap.MemberAttribute])
{
var memberDn = member.ToString();
if(userIndex.ContainsKey(memberDn) && !group.UserMemberExternalIds.Contains(userIndex[memberDn]))
{
group.UserMemberExternalIds.Add(userIndex[memberDn]);
}
else if(!group.GroupMemberReferenceIds.Contains(memberDn))
{
group.GroupMemberReferenceIds.Add(memberDn);
}
}
}
return group;
}
private static Task<List<UserEntry>> GetUsersAsync(bool force = false)
{
if(!SettingsService.Instance.Sync.SyncUsers)
{
throw new ApplicationException("Not configured to sync users.");
}
if(SettingsService.Instance.Server?.Ldap == null)
{
throw new ApplicationException("No configuration for directory server.");
}
if(SettingsService.Instance.Sync == null)
{
throw new ApplicationException("No configuration for sync.");
}
if(!AuthService.Instance.Authenticated)
{
throw new ApplicationException("Not authenticated.");
}
var entry = SettingsService.Instance.Server.Ldap.GetUserDirectoryEntry();
var filter = BuildBaseFilter(SettingsService.Instance.Sync.Ldap.UserObjectClass,
SettingsService.Instance.Sync.UserFilter);
filter = BuildRevisionFilter(filter, force, SettingsService.Instance.LastUserSyncDate);
var searcher = new DirectorySearcher(entry, filter);
var result = searcher.FindAll();
var users = new List<UserEntry>();
foreach(SearchResult item in result)
{
var user = BuildUser(item, false);
if(user == null)
{
continue;
}
users.Add(user);
}
// Deleted users
if(SettingsService.Instance.Server.Type == DirectoryType.ActiveDirectory)
{
var deletedEntry = SettingsService.Instance.Server.Ldap.GetBasePathDirectoryEntry();
var deletedFilter = BuildBaseFilter(SettingsService.Instance.Sync.Ldap.UserObjectClass, "(isDeleted=TRUE)");
deletedFilter = BuildRevisionFilter(deletedFilter, force, SettingsService.Instance.LastUserSyncDate);
var deletedSearcher = new DirectorySearcher(deletedEntry, deletedFilter);
deletedSearcher.Tombstone = true;
var deletedResult = deletedSearcher.FindAll();
foreach(SearchResult item in deletedResult)
{
var user = BuildUser(item, true);
if(user == null)
{
continue;
}
users.Add(user);
}
}
return Task.FromResult(users);
}
private static string BuildBaseFilter(string objectClass, string subFilter)
{
var filter = BuildObjectClassFilter(objectClass);
if(!string.IsNullOrWhiteSpace(subFilter))
{
filter = string.Format("(&{0}{1})", filter, subFilter);
}
return filter;
}
private static string BuildObjectClassFilter(string objectClass)
{
return string.Format("(&(objectClass={0}))", objectClass);
}
private static string BuildRevisionFilter(string baseFilter, bool force, DateTime? lastRevisionDate)
{
if(!force && lastRevisionDate.HasValue &&
!string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.Ldap.RevisionDateAttribute))
{
baseFilter = string.Format("(&{0}({1}>={2}))",
baseFilter,
SettingsService.Instance.Sync.Ldap.RevisionDateAttribute,
lastRevisionDate.Value.ToGeneralizedTimeUTC());
}
return baseFilter;
}
private static UserEntry BuildUser(SearchResult item, bool deleted)
{
var user = new UserEntry
{
ReferenceId = DNFromPath(item.Path),
Deleted = deleted
};
if(user.ReferenceId == null)
{
return null;
}
// External Id
if(item.Properties.Contains("objectGUID") && item.Properties["objectGUID"].Count > 0)
{
user.ExternalId = item.Properties["objectGUID"][0].FromGuidToString();
}
else
{
user.ExternalId = user.ReferenceId;
}
user.Disabled = EntryDisabled(item);
// Email
if(item.Properties.Contains(SettingsService.Instance.Sync.Ldap.UserEmailAttribute) &&
item.Properties[SettingsService.Instance.Sync.Ldap.UserEmailAttribute].Count > 0)
{
user.Email = item.Properties[SettingsService.Instance.Sync.Ldap.UserEmailAttribute][0]
.ToString()
.ToLowerInvariant();
}
if(string.IsNullOrWhiteSpace(user.Email) && SettingsService.Instance.Sync.Ldap.EmailPrefixSuffix &&
item.Properties.Contains(SettingsService.Instance.Sync.Ldap.UserEmailPrefixAttribute) &&
item.Properties[SettingsService.Instance.Sync.Ldap.UserEmailPrefixAttribute].Count > 0 &&
!string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.Ldap.UserEmailSuffix))
{
user.Email = string.Concat(
item.Properties[SettingsService.Instance.Sync.Ldap.UserEmailPrefixAttribute][0].ToString(),
SettingsService.Instance.Sync.Ldap.UserEmailSuffix).ToLowerInvariant();
}
if(string.IsNullOrWhiteSpace(user.Email) && !user.Deleted)
{
return null;
}
// Dates
user.CreationDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.Ldap.CreationDateAttribute);
user.RevisionDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.Ldap.RevisionDateAttribute);
return user;
}
private static bool EntryDisabled(SearchResult item)
{
if(!item.Properties.Contains("userAccountControl") || item.Properties["userAccountControl"].Count == 0)
{
return false;
}
UserAccountControl control;
if(!Enum.TryParse(item.Properties["userAccountControl"][0].ToString(), out control))
{
return false;
}
return (control & UserAccountControl.AccountDisabled) == UserAccountControl.AccountDisabled;
}
private static string DNFromPath(string path)
{
var dn = new Uri(path).Segments?.LastOrDefault();
if(dn == null)
{
return null;
}
return WebUtility.UrlDecode(dn);
}
}
}

Some files were not shown because too many files have changed in this diff Show More