mirror of
https://github.com/platformio/platformio-core.git
synced 2025-07-30 10:07:14 +02:00
Refactor account module
This commit is contained in:
13
platformio/account/commands/__init__.py
Normal file
13
platformio/account/commands/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
37
platformio/account/commands/destroy.py
Normal file
37
platformio/account/commands/destroy.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient, AccountNotAuthorized
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("destroy", short_help="Destroy account")
|
||||||
|
def account_destroy_cmd():
|
||||||
|
client = AccountClient()
|
||||||
|
click.confirm(
|
||||||
|
"Are you sure you want to delete the %s user account?\n"
|
||||||
|
"Warning! All linked data will be permanently removed and can not be restored."
|
||||||
|
% client.get_logged_username(),
|
||||||
|
abort=True,
|
||||||
|
)
|
||||||
|
client.destroy_account()
|
||||||
|
try:
|
||||||
|
client.logout()
|
||||||
|
except AccountNotAuthorized:
|
||||||
|
pass
|
||||||
|
click.secho(
|
||||||
|
"User account has been destroyed.",
|
||||||
|
fg="green",
|
||||||
|
)
|
29
platformio/account/commands/forgot.py
Normal file
29
platformio/account/commands/forgot.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("forgot", short_help="Forgot password")
|
||||||
|
@click.option("--username", prompt="Username or email")
|
||||||
|
def account_forgot_cmd(username):
|
||||||
|
client = AccountClient()
|
||||||
|
client.forgot_password(username)
|
||||||
|
click.secho(
|
||||||
|
"If this account is registered, we will send the "
|
||||||
|
"further instructions to your email.",
|
||||||
|
fg="green",
|
||||||
|
)
|
26
platformio/account/commands/login.py
Normal file
26
platformio/account/commands/login.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("login", short_help="Log in to PlatformIO Account")
|
||||||
|
@click.option("-u", "--username", prompt="Username or email")
|
||||||
|
@click.option("-p", "--password", prompt=True, hide_input=True)
|
||||||
|
def account_login_cmd(username, password):
|
||||||
|
client = AccountClient()
|
||||||
|
client.login(username, password)
|
||||||
|
click.secho("Successfully logged in!", fg="green")
|
24
platformio/account/commands/logout.py
Normal file
24
platformio/account/commands/logout.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("logout", short_help="Log out of PlatformIO Account")
|
||||||
|
def account_logout_cmd():
|
||||||
|
client = AccountClient()
|
||||||
|
client.logout()
|
||||||
|
click.secho("Successfully logged out!", fg="green")
|
26
platformio/account/commands/password.py
Normal file
26
platformio/account/commands/password.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("password", short_help="Change password")
|
||||||
|
@click.option("--old-password", prompt=True, hide_input=True)
|
||||||
|
@click.option("--new-password", prompt=True, hide_input=True, confirmation_prompt=True)
|
||||||
|
def account_password_cmd(old_password, new_password):
|
||||||
|
client = AccountClient()
|
||||||
|
client.change_password(old_password, new_password)
|
||||||
|
click.secho("Password successfully changed!", fg="green")
|
52
platformio/account/commands/register.py
Normal file
52
platformio/account/commands/register.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
|
from platformio.account.helpers import (
|
||||||
|
validate_email,
|
||||||
|
validate_password,
|
||||||
|
validate_username,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("register", short_help="Create new PlatformIO Account")
|
||||||
|
@click.option(
|
||||||
|
"-u",
|
||||||
|
"--username",
|
||||||
|
prompt=True,
|
||||||
|
callback=lambda _, __, value: validate_username(value),
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-e", "--email", prompt=True, callback=lambda _, __, value: validate_email(value)
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-p",
|
||||||
|
"--password",
|
||||||
|
prompt=True,
|
||||||
|
hide_input=True,
|
||||||
|
confirmation_prompt=True,
|
||||||
|
callback=lambda _, __, value: validate_password(value),
|
||||||
|
)
|
||||||
|
@click.option("--firstname", prompt=True)
|
||||||
|
@click.option("--lastname", prompt=True)
|
||||||
|
def account_register_cmd(username, email, password, firstname, lastname):
|
||||||
|
client = AccountClient()
|
||||||
|
client.registration(username, email, password, firstname, lastname)
|
||||||
|
click.secho(
|
||||||
|
"An account has been successfully created. "
|
||||||
|
"Please check your mail to activate your account and verify your email address.",
|
||||||
|
fg="green",
|
||||||
|
)
|
116
platformio/account/commands/show.py
Normal file
116
platformio/account/commands/show.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import click
|
||||||
|
from tabulate import tabulate
|
||||||
|
|
||||||
|
from platformio import util
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("show", short_help="PlatformIO Account information")
|
||||||
|
@click.option("--offline", is_flag=True)
|
||||||
|
@click.option("--json-output", is_flag=True)
|
||||||
|
def account_show_cmd(offline, json_output):
|
||||||
|
client = AccountClient()
|
||||||
|
info = client.get_account_info(offline)
|
||||||
|
if json_output:
|
||||||
|
click.echo(json.dumps(info))
|
||||||
|
return
|
||||||
|
click.echo()
|
||||||
|
if info.get("profile"):
|
||||||
|
print_profile(info["profile"])
|
||||||
|
if info.get("packages"):
|
||||||
|
print_packages(info["packages"])
|
||||||
|
if info.get("subscriptions"):
|
||||||
|
print_subscriptions(info["subscriptions"])
|
||||||
|
click.echo()
|
||||||
|
|
||||||
|
|
||||||
|
def print_profile(profile):
|
||||||
|
click.secho("Profile", fg="cyan", bold=True)
|
||||||
|
click.echo("=" * len("Profile"))
|
||||||
|
data = []
|
||||||
|
if profile.get("username"):
|
||||||
|
data.append(("Username:", profile["username"]))
|
||||||
|
if profile.get("email"):
|
||||||
|
data.append(("Email:", profile["email"]))
|
||||||
|
if profile.get("firstname"):
|
||||||
|
data.append(("First name:", profile["firstname"]))
|
||||||
|
if profile.get("lastname"):
|
||||||
|
data.append(("Last name:", profile["lastname"]))
|
||||||
|
click.echo(tabulate(data, tablefmt="plain"))
|
||||||
|
|
||||||
|
|
||||||
|
def print_packages(packages):
|
||||||
|
click.echo()
|
||||||
|
click.secho("Packages", fg="cyan")
|
||||||
|
click.echo("=" * len("Packages"))
|
||||||
|
for package in packages:
|
||||||
|
click.echo()
|
||||||
|
click.secho(package.get("name"), bold=True)
|
||||||
|
click.echo("-" * len(package.get("name")))
|
||||||
|
if package.get("description"):
|
||||||
|
click.echo(package.get("description"))
|
||||||
|
data = []
|
||||||
|
expire = "-"
|
||||||
|
if "subscription" in package:
|
||||||
|
expire = util.parse_datetime(
|
||||||
|
package["subscription"].get("end_at")
|
||||||
|
or package["subscription"].get("next_bill_at")
|
||||||
|
).strftime("%Y-%m-%d")
|
||||||
|
data.append(("Expire:", expire))
|
||||||
|
services = []
|
||||||
|
for key in package:
|
||||||
|
if not key.startswith("service."):
|
||||||
|
continue
|
||||||
|
if isinstance(package[key], dict):
|
||||||
|
services.append(package[key].get("title"))
|
||||||
|
else:
|
||||||
|
services.append(package[key])
|
||||||
|
if services:
|
||||||
|
data.append(("Services:", ", ".join(services)))
|
||||||
|
click.echo(tabulate(data, tablefmt="plain"))
|
||||||
|
|
||||||
|
|
||||||
|
def print_subscriptions(subscriptions):
|
||||||
|
click.echo()
|
||||||
|
click.secho("Subscriptions", fg="cyan")
|
||||||
|
click.echo("=" * len("Subscriptions"))
|
||||||
|
for subscription in subscriptions:
|
||||||
|
click.echo()
|
||||||
|
click.secho(subscription.get("product_name"), bold=True)
|
||||||
|
click.echo("-" * len(subscription.get("product_name")))
|
||||||
|
data = [("State:", subscription.get("status"))]
|
||||||
|
begin_at = util.parse_datetime(subscription.get("begin_at")).strftime("%c")
|
||||||
|
data.append(("Start date:", begin_at or "-"))
|
||||||
|
end_at = subscription.get("end_at")
|
||||||
|
if end_at:
|
||||||
|
end_at = util.parse_datetime(subscription.get("end_at")).strftime("%c")
|
||||||
|
data.append(("End date:", end_at or "-"))
|
||||||
|
next_bill_at = subscription.get("next_bill_at")
|
||||||
|
if next_bill_at:
|
||||||
|
next_bill_at = util.parse_datetime(
|
||||||
|
subscription.get("next_bill_at")
|
||||||
|
).strftime("%c")
|
||||||
|
data.append(("Next payment:", next_bill_at or "-"))
|
||||||
|
data.append(
|
||||||
|
("Edit:", click.style(subscription.get("update_url"), fg="blue") or "-")
|
||||||
|
)
|
||||||
|
data.append(
|
||||||
|
("Cancel:", click.style(subscription.get("cancel_url"), fg="blue") or "-")
|
||||||
|
)
|
||||||
|
click.echo(tabulate(data, tablefmt="plain"))
|
32
platformio/account/commands/token.py
Normal file
32
platformio/account/commands/token.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("token", short_help="Get or regenerate Authentication Token")
|
||||||
|
@click.option("-p", "--password", prompt=True, hide_input=True)
|
||||||
|
@click.option("--regenerate", is_flag=True)
|
||||||
|
@click.option("--json-output", is_flag=True)
|
||||||
|
def account_token_cmd(password, regenerate, json_output):
|
||||||
|
client = AccountClient()
|
||||||
|
auth_token = client.auth_token(password, regenerate)
|
||||||
|
if json_output:
|
||||||
|
click.echo(json.dumps({"status": "success", "result": auth_token}))
|
||||||
|
return
|
||||||
|
click.secho("Personal Authentication Token: %s" % auth_token, fg="green")
|
59
platformio/account/commands/update.py
Normal file
59
platformio/account/commands/update.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient, AccountNotAuthorized
|
||||||
|
from platformio.account.helpers import validate_email, validate_username
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("update", short_help="Update profile information")
|
||||||
|
@click.option("--current-password", prompt=True, hide_input=True)
|
||||||
|
@click.option("--username")
|
||||||
|
@click.option("--email")
|
||||||
|
@click.option("--firstname")
|
||||||
|
@click.option("--lastname")
|
||||||
|
def account_update_cmd(current_password, **kwargs):
|
||||||
|
client = AccountClient()
|
||||||
|
profile = client.get_profile()
|
||||||
|
new_profile = profile.copy()
|
||||||
|
if not any(kwargs.values()):
|
||||||
|
for field in profile:
|
||||||
|
new_profile[field] = click.prompt(
|
||||||
|
field.replace("_", " ").capitalize(), default=profile[field]
|
||||||
|
)
|
||||||
|
if field == "email":
|
||||||
|
validate_email(new_profile[field])
|
||||||
|
if field == "username":
|
||||||
|
validate_username(new_profile[field])
|
||||||
|
else:
|
||||||
|
new_profile.update({key: value for key, value in kwargs.items() if value})
|
||||||
|
client.update_profile(new_profile, current_password)
|
||||||
|
click.secho("Profile successfully updated!", fg="green")
|
||||||
|
username_changed = new_profile["username"] != profile["username"]
|
||||||
|
email_changed = new_profile["email"] != profile["email"]
|
||||||
|
if not username_changed and not email_changed:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
client.logout()
|
||||||
|
except AccountNotAuthorized:
|
||||||
|
pass
|
||||||
|
if email_changed:
|
||||||
|
click.secho(
|
||||||
|
"Please check your mail to verify your new email address and re-login. ",
|
||||||
|
fg="yellow",
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
click.secho("Please re-login.", fg="yellow")
|
||||||
|
return None
|
48
platformio/account/helpers.py
Normal file
48
platformio/account/helpers.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
|
||||||
|
def validate_username(value, field="username"):
|
||||||
|
value = str(value).strip()
|
||||||
|
if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}$", value, flags=re.I):
|
||||||
|
raise click.BadParameter(
|
||||||
|
"Invalid %s format. "
|
||||||
|
"%s must contain only alphanumeric characters "
|
||||||
|
"or single hyphens, cannot begin or end with a hyphen, "
|
||||||
|
"and must not be longer than 38 characters."
|
||||||
|
% (field.lower(), field.capitalize())
|
||||||
|
)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_email(value):
|
||||||
|
value = str(value).strip()
|
||||||
|
if not re.match(r"^[a-z\d_.+-]+@[a-z\d\-]+\.[a-z\d\-.]+$", value, flags=re.I):
|
||||||
|
raise click.BadParameter("Invalid email address")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def validate_password(value):
|
||||||
|
value = str(value).strip()
|
||||||
|
if not re.match(r"^(?=.*[a-z])(?=.*\d).{8,}$", value):
|
||||||
|
raise click.BadParameter(
|
||||||
|
"Invalid password format. "
|
||||||
|
"Password must contain at least 8 characters"
|
||||||
|
" including a number and a lowercase letter"
|
||||||
|
)
|
||||||
|
return value
|
@ -20,7 +20,7 @@ import re
|
|||||||
import click
|
import click
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from platformio.commands.account import validate_username
|
from platformio.account.helpers import validate_username
|
||||||
from platformio.commands.team import validate_orgname_teamname
|
from platformio.commands.team import validate_orgname_teamname
|
||||||
from platformio.package.registry import RegistryClient
|
from platformio.package.registry import RegistryClient
|
||||||
|
|
||||||
|
@ -12,285 +12,33 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from tabulate import tabulate
|
|
||||||
|
|
||||||
from platformio import util
|
from platformio.account.commands.destroy import account_destroy_cmd
|
||||||
from platformio.clients.account import AccountClient, AccountNotAuthorized
|
from platformio.account.commands.forgot import account_forgot_cmd
|
||||||
|
from platformio.account.commands.login import account_login_cmd
|
||||||
|
from platformio.account.commands.logout import account_logout_cmd
|
||||||
|
from platformio.account.commands.password import account_password_cmd
|
||||||
|
from platformio.account.commands.register import account_register_cmd
|
||||||
|
from platformio.account.commands.show import account_show_cmd
|
||||||
|
from platformio.account.commands.token import account_token_cmd
|
||||||
|
from platformio.account.commands.update import account_update_cmd
|
||||||
|
|
||||||
|
|
||||||
@click.group("account", short_help="Manage PlatformIO account")
|
@click.group(
|
||||||
|
"account",
|
||||||
|
commands=[
|
||||||
|
account_destroy_cmd,
|
||||||
|
account_forgot_cmd,
|
||||||
|
account_login_cmd,
|
||||||
|
account_logout_cmd,
|
||||||
|
account_password_cmd,
|
||||||
|
account_register_cmd,
|
||||||
|
account_show_cmd,
|
||||||
|
account_token_cmd,
|
||||||
|
account_update_cmd,
|
||||||
|
],
|
||||||
|
short_help="Manage PlatformIO account",
|
||||||
|
)
|
||||||
def cli():
|
def cli():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def validate_username(value, field="username"):
|
|
||||||
value = str(value).strip()
|
|
||||||
if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}$", value, flags=re.I):
|
|
||||||
raise click.BadParameter(
|
|
||||||
"Invalid %s format. "
|
|
||||||
"%s must contain only alphanumeric characters "
|
|
||||||
"or single hyphens, cannot begin or end with a hyphen, "
|
|
||||||
"and must not be longer than 38 characters."
|
|
||||||
% (field.lower(), field.capitalize())
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def validate_email(value):
|
|
||||||
value = str(value).strip()
|
|
||||||
if not re.match(r"^[a-z\d_.+-]+@[a-z\d\-]+\.[a-z\d\-.]+$", value, flags=re.I):
|
|
||||||
raise click.BadParameter("Invalid email address")
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def validate_password(value):
|
|
||||||
value = str(value).strip()
|
|
||||||
if not re.match(r"^(?=.*[a-z])(?=.*\d).{8,}$", value):
|
|
||||||
raise click.BadParameter(
|
|
||||||
"Invalid password format. "
|
|
||||||
"Password must contain at least 8 characters"
|
|
||||||
" including a number and a lowercase letter"
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("register", short_help="Create new PlatformIO Account")
|
|
||||||
@click.option(
|
|
||||||
"-u",
|
|
||||||
"--username",
|
|
||||||
prompt=True,
|
|
||||||
callback=lambda _, __, value: validate_username(value),
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"-e", "--email", prompt=True, callback=lambda _, __, value: validate_email(value)
|
|
||||||
)
|
|
||||||
@click.option(
|
|
||||||
"-p",
|
|
||||||
"--password",
|
|
||||||
prompt=True,
|
|
||||||
hide_input=True,
|
|
||||||
confirmation_prompt=True,
|
|
||||||
callback=lambda _, __, value: validate_password(value),
|
|
||||||
)
|
|
||||||
@click.option("--firstname", prompt=True)
|
|
||||||
@click.option("--lastname", prompt=True)
|
|
||||||
def account_register(username, email, password, firstname, lastname):
|
|
||||||
client = AccountClient()
|
|
||||||
client.registration(username, email, password, firstname, lastname)
|
|
||||||
click.secho(
|
|
||||||
"An account has been successfully created. "
|
|
||||||
"Please check your mail to activate your account and verify your email address.",
|
|
||||||
fg="green",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("login", short_help="Log in to PlatformIO Account")
|
|
||||||
@click.option("-u", "--username", prompt="Username or email")
|
|
||||||
@click.option("-p", "--password", prompt=True, hide_input=True)
|
|
||||||
def account_login(username, password):
|
|
||||||
client = AccountClient()
|
|
||||||
client.login(username, password)
|
|
||||||
click.secho("Successfully logged in!", fg="green")
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("logout", short_help="Log out of PlatformIO Account")
|
|
||||||
def account_logout():
|
|
||||||
client = AccountClient()
|
|
||||||
client.logout()
|
|
||||||
click.secho("Successfully logged out!", fg="green")
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("password", short_help="Change password")
|
|
||||||
@click.option("--old-password", prompt=True, hide_input=True)
|
|
||||||
@click.option("--new-password", prompt=True, hide_input=True, confirmation_prompt=True)
|
|
||||||
def account_password(old_password, new_password):
|
|
||||||
client = AccountClient()
|
|
||||||
client.change_password(old_password, new_password)
|
|
||||||
click.secho("Password successfully changed!", fg="green")
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("token", short_help="Get or regenerate Authentication Token")
|
|
||||||
@click.option("-p", "--password", prompt=True, hide_input=True)
|
|
||||||
@click.option("--regenerate", is_flag=True)
|
|
||||||
@click.option("--json-output", is_flag=True)
|
|
||||||
def account_token(password, regenerate, json_output):
|
|
||||||
client = AccountClient()
|
|
||||||
auth_token = client.auth_token(password, regenerate)
|
|
||||||
if json_output:
|
|
||||||
click.echo(json.dumps({"status": "success", "result": auth_token}))
|
|
||||||
return
|
|
||||||
click.secho("Personal Authentication Token: %s" % auth_token, fg="green")
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("forgot", short_help="Forgot password")
|
|
||||||
@click.option("--username", prompt="Username or email")
|
|
||||||
def account_forgot(username):
|
|
||||||
client = AccountClient()
|
|
||||||
client.forgot_password(username)
|
|
||||||
click.secho(
|
|
||||||
"If this account is registered, we will send the "
|
|
||||||
"further instructions to your email.",
|
|
||||||
fg="green",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("update", short_help="Update profile information")
|
|
||||||
@click.option("--current-password", prompt=True, hide_input=True)
|
|
||||||
@click.option("--username")
|
|
||||||
@click.option("--email")
|
|
||||||
@click.option("--firstname")
|
|
||||||
@click.option("--lastname")
|
|
||||||
def account_update(current_password, **kwargs):
|
|
||||||
client = AccountClient()
|
|
||||||
profile = client.get_profile()
|
|
||||||
new_profile = profile.copy()
|
|
||||||
if not any(kwargs.values()):
|
|
||||||
for field in profile:
|
|
||||||
new_profile[field] = click.prompt(
|
|
||||||
field.replace("_", " ").capitalize(), default=profile[field]
|
|
||||||
)
|
|
||||||
if field == "email":
|
|
||||||
validate_email(new_profile[field])
|
|
||||||
if field == "username":
|
|
||||||
validate_username(new_profile[field])
|
|
||||||
else:
|
|
||||||
new_profile.update({key: value for key, value in kwargs.items() if value})
|
|
||||||
client.update_profile(new_profile, current_password)
|
|
||||||
click.secho("Profile successfully updated!", fg="green")
|
|
||||||
username_changed = new_profile["username"] != profile["username"]
|
|
||||||
email_changed = new_profile["email"] != profile["email"]
|
|
||||||
if not username_changed and not email_changed:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
client.logout()
|
|
||||||
except AccountNotAuthorized:
|
|
||||||
pass
|
|
||||||
if email_changed:
|
|
||||||
click.secho(
|
|
||||||
"Please check your mail to verify your new email address and re-login. ",
|
|
||||||
fg="yellow",
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
click.secho("Please re-login.", fg="yellow")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("destroy", short_help="Destroy account")
|
|
||||||
def account_destroy():
|
|
||||||
client = AccountClient()
|
|
||||||
click.confirm(
|
|
||||||
"Are you sure you want to delete the %s user account?\n"
|
|
||||||
"Warning! All linked data will be permanently removed and can not be restored."
|
|
||||||
% client.get_logged_username(),
|
|
||||||
abort=True,
|
|
||||||
)
|
|
||||||
client.destroy_account()
|
|
||||||
try:
|
|
||||||
client.logout()
|
|
||||||
except AccountNotAuthorized:
|
|
||||||
pass
|
|
||||||
click.secho(
|
|
||||||
"User account has been destroyed.",
|
|
||||||
fg="green",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command("show", short_help="PlatformIO Account information")
|
|
||||||
@click.option("--offline", is_flag=True)
|
|
||||||
@click.option("--json-output", is_flag=True)
|
|
||||||
def account_show(offline, json_output):
|
|
||||||
client = AccountClient()
|
|
||||||
info = client.get_account_info(offline)
|
|
||||||
if json_output:
|
|
||||||
click.echo(json.dumps(info))
|
|
||||||
return
|
|
||||||
click.echo()
|
|
||||||
if info.get("profile"):
|
|
||||||
print_profile(info["profile"])
|
|
||||||
if info.get("packages"):
|
|
||||||
print_packages(info["packages"])
|
|
||||||
if info.get("subscriptions"):
|
|
||||||
print_subscriptions(info["subscriptions"])
|
|
||||||
click.echo()
|
|
||||||
|
|
||||||
|
|
||||||
def print_profile(profile):
|
|
||||||
click.secho("Profile", fg="cyan", bold=True)
|
|
||||||
click.echo("=" * len("Profile"))
|
|
||||||
data = []
|
|
||||||
if profile.get("username"):
|
|
||||||
data.append(("Username:", profile["username"]))
|
|
||||||
if profile.get("email"):
|
|
||||||
data.append(("Email:", profile["email"]))
|
|
||||||
if profile.get("firstname"):
|
|
||||||
data.append(("First name:", profile["firstname"]))
|
|
||||||
if profile.get("lastname"):
|
|
||||||
data.append(("Last name:", profile["lastname"]))
|
|
||||||
click.echo(tabulate(data, tablefmt="plain"))
|
|
||||||
|
|
||||||
|
|
||||||
def print_packages(packages):
|
|
||||||
click.echo()
|
|
||||||
click.secho("Packages", fg="cyan")
|
|
||||||
click.echo("=" * len("Packages"))
|
|
||||||
for package in packages:
|
|
||||||
click.echo()
|
|
||||||
click.secho(package.get("name"), bold=True)
|
|
||||||
click.echo("-" * len(package.get("name")))
|
|
||||||
if package.get("description"):
|
|
||||||
click.echo(package.get("description"))
|
|
||||||
data = []
|
|
||||||
expire = "-"
|
|
||||||
if "subscription" in package:
|
|
||||||
expire = util.parse_datetime(
|
|
||||||
package["subscription"].get("end_at")
|
|
||||||
or package["subscription"].get("next_bill_at")
|
|
||||||
).strftime("%Y-%m-%d")
|
|
||||||
data.append(("Expire:", expire))
|
|
||||||
services = []
|
|
||||||
for key in package:
|
|
||||||
if not key.startswith("service."):
|
|
||||||
continue
|
|
||||||
if isinstance(package[key], dict):
|
|
||||||
services.append(package[key].get("title"))
|
|
||||||
else:
|
|
||||||
services.append(package[key])
|
|
||||||
if services:
|
|
||||||
data.append(("Services:", ", ".join(services)))
|
|
||||||
click.echo(tabulate(data, tablefmt="plain"))
|
|
||||||
|
|
||||||
|
|
||||||
def print_subscriptions(subscriptions):
|
|
||||||
click.echo()
|
|
||||||
click.secho("Subscriptions", fg="cyan")
|
|
||||||
click.echo("=" * len("Subscriptions"))
|
|
||||||
for subscription in subscriptions:
|
|
||||||
click.echo()
|
|
||||||
click.secho(subscription.get("product_name"), bold=True)
|
|
||||||
click.echo("-" * len(subscription.get("product_name")))
|
|
||||||
data = [("State:", subscription.get("status"))]
|
|
||||||
begin_at = util.parse_datetime(subscription.get("begin_at")).strftime("%c")
|
|
||||||
data.append(("Start date:", begin_at or "-"))
|
|
||||||
end_at = subscription.get("end_at")
|
|
||||||
if end_at:
|
|
||||||
end_at = util.parse_datetime(subscription.get("end_at")).strftime("%c")
|
|
||||||
data.append(("End date:", end_at or "-"))
|
|
||||||
next_bill_at = subscription.get("next_bill_at")
|
|
||||||
if next_bill_at:
|
|
||||||
next_bill_at = util.parse_datetime(
|
|
||||||
subscription.get("next_bill_at")
|
|
||||||
).strftime("%c")
|
|
||||||
data.append(("Next payment:", next_bill_at or "-"))
|
|
||||||
data.append(
|
|
||||||
("Edit:", click.style(subscription.get("update_url"), fg="blue") or "-")
|
|
||||||
)
|
|
||||||
data.append(
|
|
||||||
("Cancel:", click.style(subscription.get("cancel_url"), fg="blue") or "-")
|
|
||||||
)
|
|
||||||
click.echo(tabulate(data, tablefmt="plain"))
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
from ajsonrpc.core import JSONRPC20DispatchException
|
from ajsonrpc.core import JSONRPC20DispatchException
|
||||||
|
|
||||||
from platformio.clients.account import AccountClient
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
class AccountRPC:
|
class AccountRPC:
|
||||||
|
@ -19,8 +19,8 @@ import json
|
|||||||
import click
|
import click
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from platformio.clients.account import AccountClient
|
from platformio.account.client import AccountClient
|
||||||
from platformio.commands.account import validate_email, validate_username
|
from platformio.account.helpers import validate_email, validate_username
|
||||||
|
|
||||||
|
|
||||||
@click.group("org", short_help="Manage organizations")
|
@click.group("org", short_help="Manage organizations")
|
||||||
|
@ -16,8 +16,8 @@ from twisted.cred import credentials # pylint: disable=import-error
|
|||||||
from twisted.internet import defer, protocol, reactor # pylint: disable=import-error
|
from twisted.internet import defer, protocol, reactor # pylint: disable=import-error
|
||||||
from twisted.spread import pb # pylint: disable=import-error
|
from twisted.spread import pb # pylint: disable=import-error
|
||||||
|
|
||||||
|
from platformio.account.client import AccountClient
|
||||||
from platformio.app import get_host_id
|
from platformio.app import get_host_id
|
||||||
from platformio.clients.account import AccountClient
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteClientFactory(pb.PBClientFactory, protocol.ReconnectingClientFactory):
|
class RemoteClientFactory(pb.PBClientFactory, protocol.ReconnectingClientFactory):
|
||||||
|
@ -20,7 +20,7 @@ import re
|
|||||||
import click
|
import click
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from platformio.clients.account import AccountClient
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
|
|
||||||
def validate_orgname_teamname(value, teamname_validate=False):
|
def validate_orgname_teamname(value, teamname_validate=False):
|
||||||
|
@ -115,7 +115,7 @@ class HTTPClient(object):
|
|||||||
)
|
)
|
||||||
if with_authorization and "Authorization" not in headers:
|
if with_authorization and "Authorization" not in headers:
|
||||||
# pylint: disable=import-outside-toplevel
|
# pylint: disable=import-outside-toplevel
|
||||||
from platformio.clients.account import AccountClient
|
from platformio.account.client import AccountClient
|
||||||
|
|
||||||
headers["Authorization"] = (
|
headers["Authorization"] = (
|
||||||
"Bearer %s" % AccountClient().fetch_authentication_token()
|
"Bearer %s" % AccountClient().fetch_authentication_token()
|
||||||
|
@ -21,7 +21,7 @@ import click
|
|||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from platformio import fs
|
from platformio import fs
|
||||||
from platformio.clients.account import AccountClient
|
from platformio.account.client import AccountClient
|
||||||
from platformio.exception import UserSideException
|
from platformio.exception import UserSideException
|
||||||
from platformio.package.manifest.parser import ManifestParserFactory
|
from platformio.package.manifest.parser import ManifestParserFactory
|
||||||
from platformio.package.manifest.schema import ManifestSchema
|
from platformio.package.manifest.schema import ManifestSchema
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
|
|
||||||
from platformio.clients.account import AccountClient
|
from platformio.account.client import AccountClient
|
||||||
from platformio.package.meta import PackageSpec, PackageType
|
from platformio.package.meta import PackageSpec, PackageType
|
||||||
from platformio.package.registry import RegistryClient
|
from platformio.package.registry import RegistryClient
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
|
|
||||||
from platformio import __registry_mirror_hosts__, fs
|
from platformio import __registry_mirror_hosts__, fs
|
||||||
from platformio.clients.account import AccountClient, AccountError
|
from platformio.account.client import AccountClient, AccountError
|
||||||
from platformio.http import HTTPClient, HTTPClientError
|
from platformio.http import HTTPClient, HTTPClientError
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user