1
0
mirror of https://github.com/bitwarden/web synced 2025-12-06 00:03:28 +00:00

Compare commits

...

29 Commits

Author SHA1 Message Date
Kyle Spearrin
264759cfa0 Version bump and CNAME dist fix 2016-12-03 00:56:41 -05:00
Kyle Spearrin
b5d265526a space between badges 2016-12-01 00:15:35 -05:00
Kyle Spearrin
22290eafb8 update readme with appveyor build badge 2016-12-01 00:14:52 -05:00
Kyle Spearrin
3101e57c36 Reorganization a bit more. Updated readme with build/run instructions. 2016-12-01 00:07:03 -05:00
Kyle Spearrin
b72a52232d reorganize project folder structure and remove asp.net dependency 2016-11-30 23:50:00 -05:00
Kyle Spearrin
a5b8e703fc added node server via gulp serve task 2016-11-30 23:30:08 -05:00
Kyle Spearrin
fb26425f17 tweaks to two factor modal 2016-11-30 23:22:25 -05:00
Kyle Spearrin
3114e20aef remove any spaces from authenticator code input 2016-11-26 18:52:46 -05:00
Kyle Spearrin
a52d2f4b7a added account recovery page 2016-11-14 23:31:54 -05:00
Kyle Spearrin
34e484c377 null check recovery code 2016-11-14 22:45:01 -05:00
Kyle Spearrin
08e8e9ff64 Add recovery code information to two-step login modal, also make verification code required for all actions 2016-11-14 22:38:02 -05:00
Kyle Spearrin
ebf55390eb Added password dragon xml importer #1 2016-11-11 19:21:12 -05:00
Kyle Spearrin
c328144a58 keeper csv import (#1) 2016-11-11 00:10:16 -05:00
Kyle Spearrin
4b583bea9b version bump - 1.4.0 2016-11-09 21:50:04 -05:00
Kyle Spearrin
3f0ca412c6 index checking 2016-11-09 21:49:15 -05:00
Kyle Spearrin
0050b570b4 Added delete option to edit site modal #25 2016-11-09 19:26:02 -05:00
Kyle Spearrin
9405be03b0 Added importer for universal password manager csv format 2016-11-09 18:18:46 -05:00
Kyle Spearrin
b5b706fe06 check badDataLength 2016-11-09 00:42:19 -05:00
Kyle Spearrin
8313d9fa90 Better error handling for imports. Check for bad CSV data on lastpass (when no header row is included). 2016-11-09 00:41:17 -05:00
Kyle Spearrin
93b96b3be7 adjusted add/edit site sort functions for folders 2016-10-22 09:08:17 -04:00
blindly
50e6818f2b Sdd site button to each folder (#17) 2016-10-22 08:43:50 -04:00
Kyle Spearrin
104fb57bd8 Added support for Firefox Password Exporter Addon import. ref #1 2016-10-21 00:10:17 -04:00
Kyle Spearrin
7ba6b2f00a version bump 2016-10-20 08:04:40 -04:00
Kyle Spearrin
d8e8939eca remove old code from 1Password 1pif importer 2016-10-20 08:03:20 -04:00
Kyle Spearrin
26c5f4049d link to help site 2016-10-19 22:52:53 -04:00
Kyle Spearrin
b6c9dba0fc Added Chrome csv importer. Adjustments to 1Password importer in preparation for detecting different formats. 2016-10-19 18:22:18 -04:00
blindly
8badc1a354 Folders should be sorted in alphabetical order when adding or editing a site. 2016-10-19 06:07:10 -04:00
Kyle Spearrin
15097eb1f0 version bump 2016-10-18 22:11:54 -04:00
Kyle Spearrin
986306f811 fix imports error 2016-10-18 21:34:08 -04:00
98 changed files with 737 additions and 414 deletions

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View File

@@ -1,9 +1,26 @@
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
[![appveyor build](https://ci.appveyor.com/api/projects/status/github/bitwarden/web?branch=master&svg=true)] (https://ci.appveyor.com/project/bitwarden/web) [![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
# bitwarden Web
The bitwarden Web project is an AngularJS application that powers the web vault (https://vault.bitwarden.com/).
# Build/Run
**Requirements**
- Node.js
- Gulp
Unless you are running the [Core](https://github.com/bitwarden/core) API locally, you'll probably need to switch the
application to target the production API. Open `package.json` and set `production` to `true`.
Then run the following commands:
- `gulp build`
- `gulp serve`
You can now access the web vault at `http://localhost:4001`.
# Contribute
Code contributions are welcome! Please commit any pull requests against the `master` branch.

View File

@@ -3,34 +3,37 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{860863C9-0436-43D4-840D-FE919C9F6FFC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{14FE7221-D377-4AD5-9A9E-4541577CF05A}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
CNAME = CNAME
global.json = global.json
NuGet.Config = NuGet.Config
README.md = README.md
Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "bitwarden-web", ".", "{25BEDEF4-2CAF-445A-807D-63C17FF85694}"
ProjectSection(WebsiteProperties) = preProject
TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.6.1"
Debug.AspNetCompiler.VirtualPath = "/localhost_15509"
Debug.AspNetCompiler.PhysicalPath = "."
Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_15509\"
Debug.AspNetCompiler.Updateable = "true"
Debug.AspNetCompiler.ForceOverwrite = "true"
Debug.AspNetCompiler.FixedNames = "false"
Debug.AspNetCompiler.Debug = "True"
Release.AspNetCompiler.VirtualPath = "/localhost_15509"
Release.AspNetCompiler.PhysicalPath = "."
Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_15509\"
Release.AspNetCompiler.Updateable = "true"
Release.AspNetCompiler.ForceOverwrite = "true"
Release.AspNetCompiler.FixedNames = "false"
Release.AspNetCompiler.Debug = "False"
VWDPort = "15509"
SlnRelativePath = "."
DefaultWebSiteLanguage = "Visual C#"
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Web", "src\Web\Web.xproj", "{0BEBF47C-BA0B-48AC-B48C-718F94084AD5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0BEBF47C-BA0B-48AC-B48C-718F94084AD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BEBF47C-BA0B-48AC-B48C-718F94084AD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BEBF47C-BA0B-48AC-B48C-718F94084AD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BEBF47C-BA0B-48AC-B48C-718F94084AD5}.Release|Any CPU.Build.0 = Release|Any CPU
{25BEDEF4-2CAF-445A-807D-63C17FF85694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{25BEDEF4-2CAF-445A-807D-63C17FF85694}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0BEBF47C-BA0B-48AC-B48C-718F94084AD5} = {860863C9-0436-43D4-840D-FE919C9F6FFC}
EndGlobalSection
EndGlobal

View File

@@ -1,6 +0,0 @@
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-preview2-003121"
}
}

View File

@@ -8,25 +8,26 @@ var gulp = require('gulp'),
uglify = require('gulp-uglify'),
ghPages = require('gulp-gh-pages'),
less = require('gulp-less'),
connect = require('gulp-connect'),
ngAnnotate = require('gulp-ng-annotate'),
preprocess = require('gulp-preprocess'),
runSequence = require('run-sequence'),
merge = require('merge-stream'),
ngConfig = require('gulp-ng-config'),
settings = require('./settings.json'),
project = require('./project.json'),
project = require('./package.json'),
jshint = require('gulp-jshint'),
_ = require('lodash');
var paths = {};
paths.dist = '../../dist/';
paths.webroot = './wwwroot/'
paths.dist = './dist/';
paths.webroot = './src/'
paths.js = paths.webroot + 'js/**/*.js';
paths.minJs = paths.webroot + 'js/**/*.min.js';
paths.concatJsDest = paths.webroot + 'js/bw.min.js';
paths.libDir = paths.webroot + 'lib/';
paths.npmDir = 'node_modules/';
paths.lessDir = 'less/';
paths.lessDir = paths.webroot + 'less/';
paths.cssDir = paths.webroot + 'css/';
paths.jsDir = paths.webroot + 'js/';
@@ -189,9 +190,9 @@ function config() {
constants: _.merge({}, {
appSettings: {
version: project.version,
environment: project.environment
environment: project.production ? 'Production' : 'Development'
}
}, require('./settings.' + project.environment + '.json') || {})
}, require('./settings' + (project.production ? '.Production' : '') + '.json') || {})
}));
}
@@ -213,7 +214,7 @@ gulp.task('dist:clean', function (cb) {
gulp.task('dist:move', function () {
var moves = [
{
src: '../../CNAME',
src: './CNAME',
dest: paths.dist
},
{
@@ -324,3 +325,10 @@ gulp.task('deploy', ['dist'], function () {
return gulp.src(paths.dist + '**/*')
.pipe(ghPages({ cacheDir: paths.dist + '.publish' }));
});
gulp.task('serve', function () {
connect.server({
port: 4001,
root: ['src']
});
});

View File

@@ -1,6 +1,7 @@
{
"name": "bitwarden",
"version": "0.0.0",
"version": "1.5.0",
"production": false,
"devDependencies": {
"connect": "3.4.1",
"lodash": "4.13.1",
@@ -14,6 +15,7 @@
"gulp-preprocess": "2.0.0",
"gulp-ng-annotate": "2.0.0",
"gulp-ng-config": "1.3.1",
"gulp-connect": "5.0.0",
"jshint": "2.9.2",
"gulp-jshint": "2.0.1",
"rimraf": "2.5.2",

6
settings.json Normal file
View File

@@ -0,0 +1,6 @@
{
"appSettings": {
"rememberedEmailCookieName": "bit.rememberedEmail",
"apiUri": "http://localhost:4000"
}
}

View File

@@ -1,20 +0,0 @@
using System.IO;
using Microsoft.AspNetCore.Hosting;
namespace Bit.Web
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

@@ -1,23 +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("Bit.Web")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("bitwarden Web")]
[assembly: AssemblyProduct("bitwarden Web")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[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("0bebf47c-ba0b-48ac-b48c-718f94084ad5")]

View File

@@ -1,27 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:4001/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Web": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -1,16 +0,0 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Web
{
public class Startup
{
public void ConfigureServices(IServiceCollection services) { }
public void Configure(IApplicationBuilder app)
{
app.UseFileServer();
}
}
}

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>0bebf47c-ba0b-48ac-b48c-718f94084ad5</ProjectGuid>
<RootNamespace>Bit.Vault</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>4001</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -1,47 +0,0 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,300italic,400italic,600italic);
@import "../node_modules/toastr/toastr.less";
/* Start AdminLTE */
//Bootstrap Variables & Mixins
//The core bootstrap code have not been modified. These files
//are included only for reference.
@import (reference) "../node_modules/admin-lte/build/bootstrap-less/mixins.less";
@import (reference) "../node_modules/admin-lte/build/bootstrap-less/variables.less";
//MISC
//----
@import "../node_modules/admin-lte/build/less/core.less";
@import "../node_modules/admin-lte/build/less/variables.less";
@import "../node_modules/admin-lte/build/less/mixins.less";
//COMPONENTS
//-----------
@import "../node_modules/admin-lte/build/less/header.less";
@import "../node_modules/admin-lte/build/less/sidebar.less";
@import "../node_modules/admin-lte/build/less/sidebar-mini.less";
@import "../node_modules/admin-lte/build/less/control-sidebar.less";
@import "../node_modules/admin-lte/build/less/dropdown.less";
@import "../node_modules/admin-lte/build/less/forms.less";
@import "../node_modules/admin-lte/build/less/progress-bars.less";
@import "../node_modules/admin-lte/build/less/small-box.less";
@import "../node_modules/admin-lte/build/less/boxes.less";
@import "../node_modules/admin-lte/build/less/info-box.less";
@import "../node_modules/admin-lte/build/less/timeline.less";
@import "../node_modules/admin-lte/build/less/buttons.less";
@import "../node_modules/admin-lte/build/less/callout.less";
@import "../node_modules/admin-lte/build/less/alerts.less";
@import "../node_modules/admin-lte/build/less/navs.less";
@import "../node_modules/admin-lte/build/less/table.less";
@import "../node_modules/admin-lte/build/less/labels.less";
@import "../node_modules/admin-lte/build/less/modal.less";
//PAGES
//------
@import "../node_modules/admin-lte/build/less/login_and_register.less";
@import "../node_modules/admin-lte/build/less/404_500_errors.less";
//Miscellaneous
//-------------
@import "../node_modules/admin-lte/build/less/miscellaneous.less";
@import "../node_modules/admin-lte/build/less/print.less";
/* End AdminLTE */
@import "../node_modules/admin-lte/build/less/skins/skin-blue.less";

View File

@@ -1,56 +0,0 @@
{
"version": "1.2.1",
"environment": "Development",
"dependencies": {
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.AspNetCore.StaticFiles": "1.0.0"
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
"version": "1.0.0-preview2-final",
"imports": "portable-net45+win8+dnxcore50"
}
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
}
}
}
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"runtimeOptions": {
"gcServer": false,
"gcConcurrent": true
},
"publishOptions": {
"include": [
"wwwroot",
"Views",
"settings.json",
"settings.Development.json",
"settings.Production.json",
"settings.Staging.json",
"web.config"
]
},
"scripts": {
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
},
"userSecretsId": "aspnet-Vault-20160519103145"
}

View File

@@ -1,5 +0,0 @@
{
"appSettings": {
"apiUri": "http://localhost:4000"
}
}

View File

@@ -1,5 +0,0 @@
{
"appSettings": {
"apiUri": "https://api.bitwarden.com"
}
}

View File

@@ -1,5 +0,0 @@
{
"appSettings": {
"rememberedEmailCookieName": "bit.rememberedEmail"
}
}

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>

View File

@@ -1,73 +0,0 @@
/// <autosync enabled="true" />
/// <reference path="../gulpfile.js" />
/// <reference path="app/accounts/accountsLoginController.js" />
/// <reference path="app/accounts/accountsLogoutController.js" />
/// <reference path="app/accounts/accountsmodule.js" />
/// <reference path="app/accounts/accountspasswordhintcontroller.js" />
/// <reference path="app/accounts/accountsRegisterController.js" />
/// <reference path="app/apiInterceptor.js" />
/// <reference path="app/app.js" />
/// <reference path="app/config.js" />
/// <reference path="app/directives/apiFieldDirective.js" />
/// <reference path="app/directives/apiFormDirective.js" />
/// <reference path="app/directives/directivesModule.js" />
/// <reference path="app/directives/masterPasswordDirective.js" />
/// <reference path="app/directives/pageTitleDirective.js" />
/// <reference path="app/directives/passwordmeterdirective.js" />
/// <reference path="app/directives/passwordviewerdirective.js" />
/// <reference path="app/global/globalModule.js" />
/// <reference path="app/global/mainController.js" />
/// <reference path="app/global/sideNavController.js" />
/// <reference path="app/global/topNavController.js" />
/// <reference path="app/services/apiService.js" />
/// <reference path="app/services/authService.js" />
/// <reference path="app/services/cipherService.js" />
/// <reference path="app/services/cryptoService.js" />
/// <reference path="app/services/importservice.js" />
/// <reference path="app/services/passwordservice.js" />
/// <reference path="app/services/servicesModule.js" />
/// <reference path="app/services/tokenService.js" />
/// <reference path="app/services/validationservice.js" />
/// <reference path="app/settings.js" />
/// <reference path="app/settings/settingsChangeEmailController.js" />
/// <reference path="app/settings/settingsChangePasswordController.js" />
/// <reference path="app/settings/settingsController.js" />
/// <reference path="app/settings/settingsdeletecontroller.js" />
/// <reference path="app/settings/settingsmodule.js" />
/// <reference path="app/settings/settingsSessionsController.js" />
/// <reference path="app/settings/settingsTwoFactorController.js" />
/// <reference path="app/tools/toolsAuditsController.js" />
/// <reference path="app/tools/toolsController.js" />
/// <reference path="app/tools/toolsExportController.js" />
/// <reference path="app/tools/toolsImportController.js" />
/// <reference path="app/tools/toolsmodule.js" />
/// <reference path="app/vault/vaultAddFolderController.js" />
/// <reference path="app/vault/vaultAddSiteController.js" />
/// <reference path="app/vault/vaultController.js" />
/// <reference path="app/vault/vaultEditFolderController.js" />
/// <reference path="app/vault/vaultEditSiteController.js" />
/// <reference path="app/vault/vaultmodule.js" />
/// <reference path="lib/admin-lte/js/app.js" />
/// <reference path="lib/angular/angular.js" />
/// <reference path="lib/angular-bootstrap/angular-bootstrap-tpls.js" />
/// <reference path="lib/angular-bootstrap-show-errors/showErrors.js" />
/// <reference path="lib/angular-cookies/angular-cookies.js" />
/// <reference path="lib/angular-jwt/angular-jwt.js" />
/// <reference path="lib/angular-md5/angular-md5.js" />
/// <reference path="lib/angular-messages/angular-messages.js" />
/// <reference path="lib/angular-resource/angular-resource.js" />
/// <reference path="lib/angulartics/angulartics.js" />
/// <reference path="lib/angulartics/angulartics-ga.js" />
/// <reference path="lib/angular-toastr/angular-toastr.js" />
/// <reference path="lib/angular-toastr/angular-toastr.min.js" />
/// <reference path="lib/angular-toastr/angular-toastr.tpls.min.js" />
/// <reference path="lib/angular-ui-router/angular-ui-router.js" />
/// <reference path="lib/bootstrap/js/bootstrap.min.js" />
/// <reference path="lib/clipboard/clipboard.js" />
/// <reference path="lib/jquery/jquery.js" />
/// <reference path="lib/ngclipboard/ngclipboard.js" />
/// <reference path="lib/ngstorage/ngStorage.js" />
/// <reference path="lib/papaparse/papaparse.js" />
/// <reference path="lib/sjcl/bitArray.js" />
/// <reference path="lib/sjcl/cbc.js" />
/// <reference path="lib/sjcl/sjcl.js" />

View File

@@ -1,2 +0,0 @@
angular.module("bit")
.constant("appSettings", {"rememberedEmailCookieName":"bit.rememberedEmail","version":"1.2.1","environment":"Development","apiUri":"http://localhost:4000"});

View File

@@ -0,0 +1,21 @@
angular
.module('bit.accounts')
.controller('accountsRecoverController', function ($scope, apiService, cryptoService) {
$scope.success = false;
$scope.submit = function (model) {
var email = model.email.toLowerCase();
var key = cryptoService.makeKey(model.masterPassword, email);
var request = {
email: email,
masterPasswordHash: cryptoService.hashPassword(model.masterPassword, key),
recoveryCode: model.code.replace(/\s/g, '').toLowerCase()
};
$scope.submitPromise = apiService.accounts.postTwoFactorRecover(request, function () {
$scope.success = true;
}).$promise;
};
});

View File

@@ -13,7 +13,10 @@
<span class="fa fa-lock form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-offset-7 col-xs-5">
<div class="col-xs-7">
<a ui-sref="frontend.recover">Lost authenticator app?</a>
</div>
<div class="col-xs-5">
<button type="submit" class="btn btn-primary btn-block btn-flat" ng-disabled="twoFactorForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="twoFactorForm.$loading"></i>Log In
</button>

View File

@@ -0,0 +1,52 @@
<div class="login-box">
<div class="login-logo">
<i class="fa fa-shield"></i> <b>bit</b>warden
</div>
<div class="login-box-body">
<p class="login-box-msg">Lost your authenticator app?</p>
<div class="text-center" ng-show="success">
<div class="callout callout-success">
Two-step login has been successfully disabled on your account.
</div>
<a ui-sref="frontend.login.info">Ready to log in?</a>
</div>
<form name="recoverForm" ng-submit="recoverForm.$valid && submit(model)" ng-show="!success"
api-form="submitPromise">
<div class="callout callout-danger validation-errors" ng-show="recoverForm.$errors">
<h4>Errors have occured</h4>
<ul>
<li ng-repeat="e in recoverForm.$errors">{{e}}</li>
</ul>
</div>
<div class="form-group has-feedback" show-errors>
<label for="email" class="sr-only">Email</label>
<input type="email" id="email" name="Email" class="form-control" placeholder="Email" ng-model="model.email"
required api-field />
<span class="fa fa-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback" show-errors>
<label for="masterPassword" class="sr-only">Master Password</label>
<input type="password" id="masterPassword" name="MasterPasswordHash" class="form-control" placeholder="Master Password"
ng-model="model.masterPassword"
required api-field />
<span class="fa fa-lock form-control-feedback"></span>
</div>
<div class="form-group has-feedback" show-errors>
<label for="code" class="sr-only">Recovery code</label>
<input type="text" id="code" name="RecoveryCode" class="form-control" placeholder="Recovery code"
ng-model="model.code" required api-field />
<span class="fa fa-key form-control-feedback"></span>
</div>
<div class="row">
<div class="col-xs-7">
<a ui-sref="frontend.login.info">Ready to log in?</a>
</div>
<div class="col-xs-5">
<button type="submit" class="btn btn-primary btn-block btn-flat" ng-disabled="recoverForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="recoverForm.$loading"></i>Submit
</button>
</div>
</div>
</form>
</div>
</div>

View File

@@ -105,6 +105,15 @@ angular
bodyClass: 'login-page'
}
})
.state('frontend.recover', {
url: '^/recover',
templateUrl: 'app/accounts/views/accountsRecover.html',
controller: 'accountsRecoverController',
data: {
pageTitle: 'Recover Account',
bodyClass: 'login-page'
}
})
.state('frontend.register', {
url: '^/register',
templateUrl: 'app/accounts/views/accountsRegister.html',

View File

@@ -38,6 +38,7 @@
putProfile: { url: _apiUri + '/accounts/profile', method: 'POST', params: {} },
getTwoFactor: { url: _apiUri + '/accounts/two-factor', method: 'GET', params: {} },
putTwoFactor: { url: _apiUri + '/accounts/two-factor', method: 'POST', params: {} },
postTwoFactorRecover: { url: _apiUri + '/accounts/two-factor-recover', method: 'POST', params: {} },
postPasswordHint: { url: _apiUri + '/accounts/password-hint', method: 'POST', params: {} },
putSecurityStamp: { url: _apiUri + '/accounts/security-stamp', method: 'POST', params: {} },
'import': { url: _apiUri + '/accounts/import', method: 'POST', params: {} },

View File

@@ -34,7 +34,7 @@ angular
_service.logInTwoFactor = function (code, provider) {
var request = {
code: code,
code: code.replace(' ', ''),
provider: provider
};

View File

@@ -32,6 +32,21 @@
case '1password1pif':
import1Password1Pif(file, success, error);
break;
case 'chromecsv':
importChromeCsv(file, success, error);
break;
case 'firefoxpasswordexportercsvxml':
importFirefoxPasswordExporterCsvXml(file, success, error);
break;
case 'upmcsv':
importUpmCsv(file, success, error);
break;
case 'keepercsv':
importKeeperCsv(file, success, error);
break;
case 'passworddragonxml':
importPasswordDragonXml(file, success, error);
break;
default:
error();
break;
@@ -166,7 +181,8 @@
function parseData(data) {
var folders = [],
sites = [],
siteRelationships = [];
siteRelationships = [],
badDataSites = 0;
angular.forEach(data, function (value, key) {
var folderIndex = folders.length,
@@ -184,6 +200,10 @@
}
}
if ((!value.name || value.name === '') && (!value.password || value.password === '')) {
badDataSites++;
}
sites.push({
favorite: value.fav === '1',
uri: value.url && value.url !== '' ? trimUri(value.url) : null,
@@ -208,7 +228,12 @@
}
});
success(folders, sites, siteRelationships);
if (badDataSites && badDataSites > (data.length / 2)) {
error('CSV data is not formatted correctly from LastPass. Please check your import file and try again.');
}
else {
success(folders, sites, siteRelationships);
}
}
}
@@ -556,12 +581,15 @@
reader.readAsText(file, 'utf-8');
reader.onload = function (evt) {
var fileContent = evt.target.result;
var jsonParts = fileContent.split(/(?:\r\n|\r|\n)\*\*\*.*?\*\*\*(?:\r\n|\r|\n)/);
var jsonString = '[' + jsonParts.join(',') + ']';
var items = JSON.parse(jsonString);
var fileLines = fileContent.split(/(?:\r\n|\r|\n)/);
for (i = 0; i < items.length; i++) {
var item = items[i];
for (i = 0; i < fileLines.length; i++) {
var line = fileLines[i];
if (!line.length || line[0] !== '{') {
continue;
}
var item = JSON.parse(line);
if (item.typeName !== 'webforms.WebForm') {
continue;
}
@@ -576,7 +604,7 @@
};
if (item.secureContents) {
if (item.secureContents.notesPlain) {
if (item.secureContents.notesPlain && item.secureContents.notesPlain !== '') {
site.notes = item.secureContents.notesPlain;
}
@@ -614,5 +642,345 @@
};
}
function importChromeCsv(file, success, error) {
Papa.parse(file, {
header: true,
encoding: 'UTF-8',
complete: function (results) {
parseCsvErrors(results);
var folders = [],
sites = [],
siteRelationships = [];
angular.forEach(results.data, function (value, key) {
sites.push({
favorite: false,
uri: value.url && value.url !== '' ? trimUri(value.url) : null,
username: value.username && value.username !== '' ? value.username : null,
password: value.password && value.password !== '' ? value.password : null,
notes: null,
name: value.name && value.name !== '' ? value.name : '--',
});
});
success(folders, sites, siteRelationships);
}
});
}
function importFirefoxPasswordExporterCsvXml(file, success, error) {
var folders = [],
sites = [],
siteRelationships = [];
function getNameFromHost(host) {
var name = '--';
try {
if (host && host !== '') {
var parser = document.createElement('a');
parser.href = host;
if (parser.hostname) {
name = parser.hostname;
}
}
}
catch (e) {
// do nothing
}
return name;
}
if (file.type === 'text/xml') {
var reader = new FileReader();
reader.readAsText(file, 'utf-8');
reader.onload = function (evt) {
var xmlDoc = $.parseXML(evt.target.result),
xml = $(xmlDoc);
var entries = xml.find('entry');
for (var i = 0; i < entries.length; i++) {
var entry = $(entries[i]);
if (!entry) {
continue;
}
var host = entry.attr('host'),
user = entry.attr('user'),
password = entry.attr('password');
sites.push({
favorite: false,
uri: host && host !== '' ? trimUri(host) : null,
username: user && user !== '' ? user : null,
password: password && password !== '' ? password : null,
notes: null,
name: getNameFromHost(host),
});
}
success(folders, sites, siteRelationships);
};
reader.onerror = function (evt) {
error();
};
}
else {
// currently bugged due to the comment
// ref: https://github.com/mholt/PapaParse/issues/351
error('Only .xml exports are supported.');
return;
//Papa.parse(file, {
// comments: '#',
// header: true,
// encoding: 'UTF-8',
// complete: function (results) {
// parseCsvErrors(results);
// angular.forEach(results.data, function (value, key) {
// sites.push({
// favorite: false,
// uri: value.hostname && value.hostname !== '' ? trimUri(value.hostname) : null,
// username: value.username && value.username !== '' ? value.username : null,
// password: value.password && value.password !== '' ? value.password : null,
// notes: null,
// name: getNameFromHost(value.hostname),
// });
// });
// success(folders, sites, siteRelationships);
// }
//});
}
}
function importUpmCsv(file, success, error) {
Papa.parse(file, {
encoding: 'UTF-8',
complete: function (results) {
parseCsvErrors(results);
var folders = [],
sites = [],
siteRelationships = [];
angular.forEach(results.data, function (value, key) {
if (value.length === 5) {
sites.push({
favorite: false,
uri: value[3] && value[3] !== '' ? trimUri(value[3]) : null,
username: value[1] && value[1] !== '' ? value[1] : null,
password: value[2] && value[2] !== '' ? value[2] : null,
notes: value[4] && value[4] !== '' ? value[4] : null,
name: value[0] && value[0] !== '' ? value[0] : '--',
});
}
});
success(folders, sites, siteRelationships);
}
});
}
function importKeeperCsv(file, success, error) {
Papa.parse(file, {
encoding: 'UTF-8',
complete: function (results) {
parseCsvErrors(results);
var folders = [],
sites = [],
folderRelationships = [];
angular.forEach(results.data, function (value, key) {
if (value.length >= 6) {
var folderIndex = folders.length,
siteIndex = sites.length,
hasFolder = value[0] && value[0] !== '',
addFolder = hasFolder,
i = 0;
if (hasFolder) {
for (i = 0; i < folders.length; i++) {
if (folders[i].name === value[0]) {
addFolder = false;
folderIndex = i;
break;
}
}
}
var site = {
favorite: false,
uri: value[4] && value[4] !== '' ? trimUri(value[4]) : null,
username: value[2] && value[2] !== '' ? value[2] : null,
password: value[3] && value[3] !== '' ? value[3] : null,
notes: value[5] && value[5] !== '' ? value[5] : null,
name: value[1] && value[1] !== '' ? value[1] : '--',
};
if (value.length > 6) {
// we have some custom fields. add them to notes.
if (site.notes === null) {
site.notes = '';
}
else {
site.notes += '\n';
}
for (i = 6; i < value.length; i = i + 2) {
var cfName = value[i];
var cfValue = value[i + 1];
site.notes += (cfName + ': ' + cfValue + '\n');
}
}
sites.push(site);
if (addFolder) {
folders.push({
name: value[0]
});
}
if (hasFolder) {
var relationship = {
key: siteIndex,
value: folderIndex
};
folderRelationships.push(relationship);
}
}
});
success(folders, sites, folderRelationships);
}
});
}
function importPasswordDragonXml(file, success, error) {
var folders = [],
sites = [],
folderRelationships = [],
foldersIndex = [],
j = 0;
var reader = new FileReader();
reader.readAsText(file, 'utf-8');
reader.onload = function (evt) {
var xmlDoc = $.parseXML(evt.target.result),
xml = $(xmlDoc);
var pwManager = xml.find('PasswordManager');
if (pwManager.length) {
var records = pwManager.find('> record');
if (records.length) {
for (var i = 0; i < records.length; i++) {
var record = $(records[i]);
var accountNameNode = record.find('> Account-Name'),
accountName = accountNameNode.length ? $(accountNameNode) : null,
userIdNode = record.find('> User-Id'),
userId = userIdNode.length ? $(userIdNode) : null,
passwordNode = record.find('> Password'),
password = passwordNode.length ? $(passwordNode) : null,
urlNode = record.find('> URL'),
url = urlNode.length ? $(urlNode) : null,
notesNode = record.find('> Notes'),
notes = notesNode.length ? $(notesNode) : null,
categoryNode = record.find('> Category'),
category = categoryNode.length ? $(categoryNode) : null,
categoryText = category ? category.text() : null;
var folderIndex = folders.length,
siteIndex = sites.length,
hasFolder = categoryText && categoryText !== '' && categoryText !== 'Unfiled',
addFolder = hasFolder;
if (hasFolder) {
for (j = 0; j < folders.length; j++) {
if (folders[j].name === categoryText) {
addFolder = false;
folderIndex = j;
break;
}
}
}
var site = {
favorite: false,
uri: url && url.text() !== '' ? trimUri(url.text()) : null,
username: userId && userId.text() !== '' ? userId.text() : null,
password: password && password.text() !== '' ? password.text() : null,
notes: notes && notes.text() !== '' ? notes.text() : null,
name: accountName && accountName.text() !== '' ? accountName.text() : '--',
};
var attributesSelector = '';
for (j = 1; j <= 10; j++) {
attributesSelector += '> Attribute-' + j;
if (j < 10) {
attributesSelector += ', ';
}
}
var attributes = record.find(attributesSelector);
if (attributes.length) {
// we have some attributes. add them to notes.
for (j = 0; j < attributes.length; j++) {
var attr = $(attributes[j]),
attrName = attr.prop('tagName'),
attrValue = attr.text();
if (!attrValue || attrValue === '' || attrValue === 'null') {
continue;
}
if (site.notes === null) {
site.notes = '';
}
else {
site.notes += '\n';
}
site.notes += (attrName + ': ' + attrValue);
}
}
sites.push(site);
if (addFolder) {
folders.push({
name: categoryText
});
}
if (hasFolder) {
var relationship = {
key: siteIndex,
value: folderIndex
};
folderRelationships.push(relationship);
}
}
}
success(folders, sites, folderRelationships);
}
else {
error();
}
};
reader.onerror = function (evt) {
error();
};
}
return _service;
});

2
src/app/settings.js Normal file
View File

@@ -0,0 +1,2 @@
angular.module("bit")
.constant("appSettings", {"rememberedEmailCookieName":"bit.rememberedEmail","apiUri":"http://localhost:4000","version":"1.5.0","environment":"Development"});

View File

@@ -19,18 +19,31 @@
masterPasswordHash: _masterPasswordHash,
provider: 0 /* Only authenticator provider for now. */
}, function (response) {
var key = response.AuthenticatorKey;
$scope.twoFactorModel = {
enabled: response.TwoFactorEnabled,
key: key.replace(/(.{4})/g, '$1 ').trim(),
qr: 'https://chart.googleapis.com/chart?chs=120x120&chld=L|0&cht=qr&chl=otpauth://totp/' +
_issuer + ':' + encodeURIComponent(_profile.email) +
'%3Fsecret=' + encodeURIComponent(key) +
'%26issuer=' + _issuer
};
processResponse(response);
}).$promise;
};
function formatString(s) {
if (!s) {
return null;
}
return s.replace(/(.{4})/g, '$1 ').trim().toUpperCase();
}
function processResponse(response) {
var key = response.AuthenticatorKey;
$scope.twoFactorModel = {
enabled: response.TwoFactorEnabled,
key: formatString(key),
recovery: formatString(response.TwoFactorRecoveryCode),
qr: 'https://chart.googleapis.com/chart?chs=120x120&chld=L|0&cht=qr&chl=otpauth://totp/' +
_issuer + ':' + encodeURIComponent(_profile.email) +
'%3Fsecret=' + encodeURIComponent(key) +
'%26issuer=' + _issuer
};
}
$scope.update = function (model) {
var currentlyEnabled = $scope.twoFactorModel.enabled;
if (currentlyEnabled && !confirm('Are you sure you want to disable two-step login?')) {
@@ -39,7 +52,7 @@
var request = {
enabled: !currentlyEnabled,
token: model ? model.token : null,
token: model.token.replace(' ', ''),
masterPasswordHash: _masterPasswordHash
};
@@ -48,14 +61,16 @@
$analytics.eventTrack('Enabled Two-step Login');
toastr.success('Two-step login has been enabled.');
if (_profile.extended) _profile.extended.twoFactorEnabled = true;
processResponse(response);
$('#token').blur();
model.token = null;
}
else {
$analytics.eventTrack('Disabled Two-step Login');
toastr.success('Two-step login has been disabled.');
if (_profile.extended) _profile.extended.twoFactorEnabled = false;
$scope.close();
}
$scope.close();
}).$promise;
};

View File

@@ -29,7 +29,7 @@
<form name="updateTwoStepForm" ng-submit="updateTwoStepForm.$valid && update(updateModel)" api-form="updatePromise" ng-if="twoFactorModel">
<div class="modal-body">
<div ng-show="enabled()">
<p>Two-step login is enabled on your account. Below is the code required by your verification app.</p>
<p>Two-step login is <strong class="text-green">enabled</strong> on your account. Below is the code required by your verification app.</p>
<p>Need a two-step verification app? Download one of the following:</p>
</div>
<div ng-show="!enabled()">
@@ -37,7 +37,7 @@
<h4>1. Download a two-step verification app</h4>
</div>
<ul class="fa-ul">
<li><i class="fa-li fa fa-apple"></i> iOS devices: <a href="https://itunes.apple.com/en/app/authy/id494168017" target="_blank">Authy for iOS</a></li>
<li><i class="fa-li fa fa-apple"></i> iOS devices: <a href="https://itunes.apple.com/us/app/authy/id494168017?mt=8" target="_blank">Authy for iOS</a></li>
<li><i class="fa-li fa fa-android"></i> Android devices: <a href="https://play.google.com/store/apps/details?id=com.authy.authy" target="_blank">Authy for Android</a></li>
<li><i class="fa-li fa fa-windows"></i> Windows devices: <a href="https://www.microsoft.com/en-us/store/apps/authenticator/9wzdncrfj3rj" target="_blank">Microsoft Authenticator </a></li>
</ul>
@@ -47,35 +47,48 @@
<div class="col-md-4 text-center">
<p><img ng-src="{{twoFactorModel.qr}}" alt="QR" class="img-thumbnail" /></p>
</div>
<div class="col-sm-8">
<div class="col-md-8">
<p><strong>Can't scan the code?</strong> You can add the code to your application manually using the following details:</p>
<ul class="list-unstyled">
<li><strong>Key:</strong> <samp>{{twoFactorModel.key}}</samp></li>
<li><strong>Key:</strong> <code>{{twoFactorModel.key}}</code></li>
<li><strong>Account:</strong> {{account}}</li>
<li><strong>Time based:</strong> Yes</li>
</ul>
</div>
</div>
<div ng-show="!enabled()">
<div class="callout callout-danger validation-errors" ng-show="updateTwoStepForm.$errors">
<h4>Errors have occured</h4>
<ul>
<li ng-repeat="e in updateTwoStepForm.$errors">{{e}}</li>
</ul>
</div>
<h4 style="margin-top: 30px;">3. Enter the resulting verification code from the app</h4>
<div class="form-group" show-errors ng-show="!twoFactorModel.enabled">
<label for="token" class="sr-only">Verification Code</label>
<input type="number" id="token" name="Token" placeholder="Verification Code" ng-model="updateModel.token" class="form-control" ng-required="!twoFactorModel.enabled" api-field />
</div>
<p>NOTE: After enabling two-step login, you will be required to enter the current code generated by your verification app each time you log in.</p>
<div ng-show="enabled()">
<hr />
<h4>Recovery Code</h4>
<p>
The recovery code allows you to access your account in the event that you lose your authenticator app.
bitwarden support won't be able to assist you if you lose access to your account. We recommend you write down or
print the recovery code below and keep it in a safe place.
</p>
<ul class="list-unstyled">
<li>
<strong>Recovery Code:</strong> <code>{{twoFactorModel.recovery}}</code>
</li>
</ul>
</div>
<div class="callout callout-danger validation-errors" ng-show="updateTwoStepForm.$errors">
<h4>Errors have occured</h4>
<ul>
<li ng-repeat="e in updateTwoStepForm.$errors">{{e}}</li>
</ul>
</div>
<hr ng-show="enabled()" />
<h4 style="margin-top: 30px;"><span ng-show="!enabled()">3. </span>Enter the resulting verification code from the app</h4>
<div class="form-group" show-errors>
<label for="token" class="sr-only">Verification Code</label>
<input type="text" id="token" name="Token" placeholder="Verification Code" ng-model="updateModel.token" class="form-control" required api-field />
</div>
<p ng-show="!enabled()">NOTE: After enabling two-step login, you will be required to enter the current code generated by your verification app each time you log in.</p>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary btn-flat" ng-disabled="updateTwoStepForm.$loading">
<i class="fa fa-refresh fa-spin loading-icon" ng-show="updateTwoStepForm.$loading"></i>
<span ng-show="twoFactorModel.enabled">Disable Two-step</span>
<span ng-show="!twoFactorModel.enabled">Enable Two-step</span>
<span ng-show="enabled()">Disable Two-step</span>
<span ng-show="!enabled()">Enable Two-step</span>
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
</div>

View File

@@ -12,7 +12,8 @@
};
function importSuccess(folders, sites, folderRelationships) {
if (!folder.length && !sites.length) {
if (!folders.length && !sites.length) {
$uibModalInstance.dismiss('cancel');
toastr.error('Nothing was imported.');
return;
}
@@ -30,15 +31,40 @@
}, importError);
}
function importError(errorMessage) {
function importError(error) {
$analytics.eventTrack('Import Data Failed', { label: $scope.model.source });
$uibModalInstance.dismiss('cancel');
if (errorMessage) {
toastr.error(errorMessage);
}
else {
toastr.error('Something went wrong. Try again.', 'Oh No!');
if (error) {
var data = error.data;
if (data && data.ValidationErrors) {
var message = '';
for (var key in data.ValidationErrors) {
if (!data.ValidationErrors.hasOwnProperty(key)) {
continue;
}
for (var i = 0; i < data.ValidationErrors[key].length; i++) {
message += (key + ': ' + data.ValidationErrors[key][i] + ' ');
}
}
if (message !== '') {
toastr.error(message);
return;
}
}
else if (data && data.Message) {
toastr.error(data.Message);
return;
}
else {
toastr.error(error);
return;
}
}
toastr.error('Something went wrong. Try again.', 'Oh No!');
}
$scope.close = function () {

View File

@@ -9,11 +9,16 @@
<select id="source" name="source" class="form-control" ng-model="model.source">
<option value="local">bitwarden (csv)</option>
<option value="lastpass">LastPass (csv)</option>
<option value="chromecsv">Chrome (csv)</option>
<option value="firefoxpasswordexportercsvxml">Firefox Password Exporter (xml)</option>
<option value="safeincloudxml">SafeInCloud (xml)</option>
<option value="safeincloudcsv">SafeInCloud (csv)</option>
<option value="keypassxml">KeyPass (xml)</option>
<option value="padlockcsv">Padlock (csv)</option>
<option value="1password1pif">1Password (1pif)</option>
<option value="upmcsv">Universal Password Manager (csv)</option>
<option value="keepercsv">Keeper (csv)</option>
<option value="passworddragonxml">Password Dragon (xml)</option>
</select>
</div>
<div class="form-group">

View File

@@ -37,6 +37,10 @@
alert('Your web browser does not support easy clipboard copying. Copy it manually instead.');
};
$scope.folderSort = function (item) {
return item.name.toLowerCase();
};
function selectPassword(e) {
var target = $(e.trigger).parent().prev();
if (target.attr('type') === 'text') {

View File

@@ -77,13 +77,25 @@
}
});
editModel.result.then(function (editedSite) {
var site = $filter('filter')($scope.sites, { id: editedSite.id }, true);
if (site && site.length > 0) {
site[0].folderId = editedSite.folderId;
site[0].name = editedSite.name;
site[0].username = editedSite.username;
site[0].favorite = editedSite.favorite;
editModel.result.then(function (returnVal) {
if (returnVal.action === 'edit') {
var siteToUpdate = $filter('filter')($scope.sites, { id: returnVal.data.id }, true);
if (siteToUpdate && siteToUpdate.length > 0) {
siteToUpdate[0].folderId = returnVal.data.folderId;
siteToUpdate[0].name = returnVal.data.name;
siteToUpdate[0].username = returnVal.data.username;
siteToUpdate[0].favorite = returnVal.data.favorite;
}
}
else if (returnVal.action === 'delete') {
var siteToDelete = $filter('filter')($scope.sites, { id: returnVal.data }, true);
if (siteToDelete && siteToDelete.length > 0) {
var index = $scope.sites.indexOf(siteToDelete[0]);
if (index > -1) {
$scope.sites.splice(index, 1);
}
}
}
});
};
@@ -115,7 +127,9 @@
apiService.sites.del({ id: site.id }, function () {
var index = $scope.sites.indexOf(site);
$scope.sites.splice(index, 1);
if (index > -1) {
$scope.sites.splice(index, 1);
}
});
};
@@ -162,7 +176,9 @@
apiService.folders.del({ id: folder.id }, function () {
var index = $scope.folders.indexOf(folder);
$scope.folders.splice(index, 1);
if (index > -1) {
$scope.folders.splice(index, 1);
}
});
};

View File

@@ -15,7 +15,10 @@
$scope.savePromise = apiService.sites.put({ id: siteId }, site, function (siteResponse) {
$analytics.eventTrack('Edited Site');
var decSite = cipherService.decryptSite(siteResponse);
$uibModalInstance.close(decSite);
$uibModalInstance.close({
action: 'edit',
data: decSite
});
}).$promise;
};
@@ -38,6 +41,10 @@
alert('Your web browser does not support easy clipboard copying. Copy it manually instead.');
};
$scope.folderSort = function (item) {
return item.name.toLowerCase();
};
function selectPassword(e) {
var target = $(e.trigger).parent().prev();
if (target.attr('type') === 'text') {
@@ -45,6 +52,19 @@
}
}
$scope.delete = function () {
if (!confirm('Are you sure you want to delete this site (' + $scope.site.name + ')?')) {
return;
}
apiService.sites.del({ id: $scope.site.id }, function () {
$uibModalInstance.close({
action: 'delete',
data: $scope.site.id
});
});
};
$scope.close = function () {
$uibModalInstance.dismiss('cancel');
};

View File

@@ -12,6 +12,9 @@
<div class="box-header with-border">
<h3 class="box-title"><i class="fa fa-folder-open"></i> {{folder.name}}</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" ng-click="addSite(folder)" uib-tooltip="Add Site">
<i class="fa fa-plus"></i>
</button>
<button type="button" class="btn btn-box-tool" ng-click="deleteFolder(folder)" ng-show="canDeleteFolder(folder)" uib-tooltip="Delete">
<i class="fa fa-trash"></i>
</button>
@@ -54,4 +57,4 @@
</div>
</div>
</div>
</section>
</section>

View File

@@ -34,7 +34,7 @@
<div class="form-group" show-errors>
<label for="folder">Folder</label>
<select id="folder" name="FolderId" ng-model="site.folderId" class="form-control" api-field>
<option ng-repeat="folder in folders" value="{{folder.id}}">{{folder.name}}</option>
<option ng-repeat="folder in folders | orderBy: folderSort" value="{{folder.id}}">{{folder.name}}</option>
</select>
</div>
</div>
@@ -95,4 +95,4 @@
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
</div>
</form>
</form>

View File

@@ -37,7 +37,7 @@
<div class="form-group" show-errors>
<label for="folder">Folder</label>
<select id="folder" name="FolderId" ng-model="site.folderId" class="form-control" api-field>
<option ng-repeat="folder in folders" value="{{folder.id}}">{{folder.name}}</option>
<option ng-repeat="folder in folders | orderBy: folderSort" value="{{folder.id}}">{{folder.name}}</option>
</select>
</div>
</div>
@@ -97,5 +97,8 @@
<i class="fa fa-refresh fa-spin loading-icon" ng-show="editSiteForm.$loading"></i>Save
</button>
<button type="button" class="btn btn-default btn-flat" ng-click="close()">Close</button>
<button type="button" class="btn btn-link pull-right" ng-click="delete()" uib-tooltip="Delete">
<i class="fa fa-trash fa-lg"></i>
</button>
</div>
</form>
</form>

View File

@@ -107,7 +107,7 @@
</ul>
</li>
<li>
<a href="https://bitwarden.com/contact/" target="_blank"
<a href="https://help.bitwarden.com/" target="_blank"
analytics-on="click" analytics-event="Clicked Get Help">
<i class="fa fa-info-circle"></i> <span>Get Help</span>
</a>

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -110,6 +110,7 @@
<script src="app/accounts/accountsLogoutController.js"></script>
<script src="app/accounts/accountsRegisterController.js"></script>
<script src="app/accounts/accountsPasswordHintController.js"></script>
<script src="app/accounts/accountsRecoverController.js"></script>
<script src="app/vault/vaultModule.js"></script>
<script src="app/vault/vaultController.js"></script>

47
src/less/theme.less Normal file
View File

@@ -0,0 +1,47 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700,300italic,400italic,600italic);
@import "../../node_modules/toastr/toastr.less";
/* Start AdminLTE */
//Bootstrap Variables & Mixins
//The core bootstrap code have not been modified. These files
//are included only for reference.
@import (reference) "../../node_modules/admin-lte/build/bootstrap-less/mixins.less";
@import (reference) "../../node_modules/admin-lte/build/bootstrap-less/variables.less";
//MISC
//----
@import "../../node_modules/admin-lte/build/less/core.less";
@import "../../node_modules/admin-lte/build/less/variables.less";
@import "../../node_modules/admin-lte/build/less/mixins.less";
//COMPONENTS
//-----------
@import "../../node_modules/admin-lte/build/less/header.less";
@import "../../node_modules/admin-lte/build/less/sidebar.less";
@import "../../node_modules/admin-lte/build/less/sidebar-mini.less";
@import "../../node_modules/admin-lte/build/less/control-sidebar.less";
@import "../../node_modules/admin-lte/build/less/dropdown.less";
@import "../../node_modules/admin-lte/build/less/forms.less";
@import "../../node_modules/admin-lte/build/less/progress-bars.less";
@import "../../node_modules/admin-lte/build/less/small-box.less";
@import "../../node_modules/admin-lte/build/less/boxes.less";
@import "../../node_modules/admin-lte/build/less/info-box.less";
@import "../../node_modules/admin-lte/build/less/timeline.less";
@import "../../node_modules/admin-lte/build/less/buttons.less";
@import "../../node_modules/admin-lte/build/less/callout.less";
@import "../../node_modules/admin-lte/build/less/alerts.less";
@import "../../node_modules/admin-lte/build/less/navs.less";
@import "../../node_modules/admin-lte/build/less/table.less";
@import "../../node_modules/admin-lte/build/less/labels.less";
@import "../../node_modules/admin-lte/build/less/modal.less";
//PAGES
//------
@import "../../node_modules/admin-lte/build/less/login_and_register.less";
@import "../../node_modules/admin-lte/build/less/404_500_errors.less";
//Miscellaneous
//-------------
@import "../../node_modules/admin-lte/build/less/miscellaneous.less";
@import "../../node_modules/admin-lte/build/less/print.less";
/* End AdminLTE */
@import "../../node_modules/admin-lte/build/less/skins/skin-blue.less";