init
This commit is contained in:
commit
9e617a6f1f
45
README.md
Normal file
45
README.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Nextcloud Passwords to Bitwarden
|
||||||
|
|
||||||
|
This project allows you to convert Nextcloud Passwords CSV to a Bitwarden JSON file.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://gitnet.fr/deblan/nextcloud_passwords_to_bitwarden
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to
|
||||||
|
|
||||||
|
### On Nextcloud
|
||||||
|
|
||||||
|
* Go to "Personal settings"
|
||||||
|
* Set "English (US)" as language
|
||||||
|
* Go to "Passwords"
|
||||||
|
* Click on "More"
|
||||||
|
* Click on "Backup and restore"
|
||||||
|
* Choose "Backup or export"
|
||||||
|
* Choose "Predefined CSV"
|
||||||
|
* Check "Export Passwords" and "Export Folders"
|
||||||
|
* Click on "Export"
|
||||||
|
* Unzip the download file
|
||||||
|
|
||||||
|
### On the project
|
||||||
|
|
||||||
|
```
|
||||||
|
php index.php /path/to/Folders.csv /path/to/Passwords.csv > bitwarden_passwords.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### On Bitwarden/Vaultwarden
|
||||||
|
|
||||||
|
* Go to tools
|
||||||
|
* Go to "Import data"
|
||||||
|
* Choose "Bitwarden (json)" as file format
|
||||||
|
* Choose the file "bitwarden_passwords.json"
|
||||||
|
* Click on "Import data"
|
||||||
|
|
||||||
|
### Clean up datas
|
||||||
|
|
||||||
|
* Remove the downloaded archive
|
||||||
|
* Remove extracted files
|
||||||
|
* Remove bitwarden_passwords.json
|
5
composer.json
Normal file
5
composer.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"league/csv": "^9.15"
|
||||||
|
}
|
||||||
|
}
|
121
index.php
Normal file
121
index.php
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require __DIR__.'/vendor/autoload.php';
|
||||||
|
|
||||||
|
use League\Csv\Reader;
|
||||||
|
|
||||||
|
function getRecords(string $file): Iterator
|
||||||
|
{
|
||||||
|
$csv = Reader::createFromPath($file, 'r');
|
||||||
|
$csv->setHeaderOffset(0);
|
||||||
|
|
||||||
|
return $csv->getRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage(int $exitCode = 0, ?string $error = null): void
|
||||||
|
{
|
||||||
|
$usage = [];
|
||||||
|
|
||||||
|
if (null !== $error) {
|
||||||
|
$usage[] = $error;
|
||||||
|
$usage[] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = __FILE__;
|
||||||
|
|
||||||
|
$usage[] = 'Usage';
|
||||||
|
$usage[] = " php {$file} -h|--help";
|
||||||
|
$usage[] = " php {$file} /path/to/Folders.csv /path/to/Passwords.csv";
|
||||||
|
|
||||||
|
file_put_contents('php://stderr', implode("\n", $usage));
|
||||||
|
|
||||||
|
exit($exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkFile(?string $file): void
|
||||||
|
{
|
||||||
|
if (null === $file) {
|
||||||
|
usage(1, 'File not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_file($file) || !is_readable($file)) {
|
||||||
|
usage(1, 'File not found or not readable: '.$file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($argv as $value) {
|
||||||
|
if (in_array($value, ['-h', '--help'])) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$folders = $argv[1] ?? null;
|
||||||
|
$passwords = $argv[2] ?? null;
|
||||||
|
|
||||||
|
checkFile($folders);
|
||||||
|
checkFile($passwords);
|
||||||
|
|
||||||
|
$datas = [
|
||||||
|
'encrypted' => false,
|
||||||
|
'folders' => [],
|
||||||
|
'items' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach (getRecords($folders) as $item) {
|
||||||
|
$datas['folders'][] = [
|
||||||
|
'id' => $item['Id'],
|
||||||
|
'name' => $item['Label'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (getRecords($passwords) as $item) {
|
||||||
|
$fields = [];
|
||||||
|
|
||||||
|
if (!empty($item['Custom Fields'])) {
|
||||||
|
$elements = explode("\n", trim($item['Custom Fields']));
|
||||||
|
|
||||||
|
if (!empty($elements)) {
|
||||||
|
foreach ($elements as $element) {
|
||||||
|
preg_match('/^(.*), (.*): (.*)$/iU', $element, $match);
|
||||||
|
|
||||||
|
$fields[] = [
|
||||||
|
'name' => $match[1],
|
||||||
|
'value' => $match[3],
|
||||||
|
'linkedId' => null,
|
||||||
|
'type' => match ($match[2]) {
|
||||||
|
'secret' => 1,
|
||||||
|
default => 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$datas['items'][] = [
|
||||||
|
'passwordHistory' => null,
|
||||||
|
'revisionDate' => null,
|
||||||
|
'creationDate' => null,
|
||||||
|
'deletedDate' => null,
|
||||||
|
'id' => $item['Id'],
|
||||||
|
'organizationId' => null,
|
||||||
|
'folderId' => $item['Folder Id'],
|
||||||
|
'type' => 1,
|
||||||
|
'reprompt' => 0,
|
||||||
|
'name' => $item['Label'],
|
||||||
|
'notes' => $item['Notes'],
|
||||||
|
'favorite' => 'true' === $item['Favorite'],
|
||||||
|
'fields' => $fields,
|
||||||
|
'login' => [
|
||||||
|
'fido2Credentials' => [],
|
||||||
|
'uris' => [[
|
||||||
|
'match' => null,
|
||||||
|
'uri' => $item['Url'],
|
||||||
|
]],
|
||||||
|
'username' => $item['Username'],
|
||||||
|
'password' => $item['Password'],
|
||||||
|
'topt' => null,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($datas);
|
Loading…
Reference in a new issue