{"action":"created","comment":{"url":"https://api.github.com/repos/openwisp/django-freeradius/pulls/comments/195016563","pull_request_review_id":128296905,"id":195016563,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5NTAxNjU2Mw==","diff_hunk":"@@ -0,0 +1,88 @@\n+===============\n+Importing users\n+===============\n+\n+This feature can be used for importing users from a csv file. There are many features included in it such as:\n+\n+* Importing users in batches: all of the users of a particular csv file would be stored in batches and can be retrieved/ deleted easily using the batch functions.\n+* Set an expiration date: Expiration date can be set for a batch after which the users would not able to authenticate to the RADIUS Server.\n+* Autogenerate usernames and passwords: The usernames and passwords are automatically generated if they aren't provided in the csv file. Usernames are generated from the email address whereas passwords are generated randomly where the length of passwords can be customized.\n+* Passwords are accepted in both cleartext and hash formats from the CSV.\n+* Send mails to users whose passwords have been generated automatically.\n+\n+It can be done using both a management command and the admin interface.\n+\n+``batch_add_users``\n+-------------------\n+\n+This command imports users from a csv file. Usage is as shown below.\n+\n+.. code-block:: shell\n+\n+    ./manage.py batch_add_users --name <name_of_batch> \\\n+                                --file <filepath> \\\n+                                --expiration <expiration_date> \\\n+                                --password-length <password_length>\n+\n+Note that the expiration and password-length are optional parameters which default to never and 8 respectively.\n+\n+``Using the admin interface``\n+-----------------------------\n+\n+Selecting the CSV as the strategy and uploading the CSV file is all one will have to do to import the CSV file from the admin interface. It can be checked at `/admin/radiusbatch/add`.\n+\n+It is important to take care of the following when importing users from the CSV.\n+\n+``CSV Format``\n+--------------\n+\n+The CSV shall be of the format::\n+\n+    username,password,email,firstname,lastname\n+\n+``Imported users with hashed passwords``\n+----------------------------------------\n+\n+The hashes are directly stored in the database if they are of the `django hash format <https://docs.djangoproject.com/en/2.0/topics/auth/passwords/>`_.\n+\n+For example, a password myPassword123, hashed using salted SHA1 algorithm, will look like::\n+\n+    pbkdf2_sha256$100000$cKdP39chT3pW$2EtVk4Hhm1V65GNfYAA5AHj0uyD60f2CmqumqiB/gRk=\n+\n+So a full CSV line containing that password would be::\n+\n+    username,pbkdf2_sha256$100000$cKdP39chT3pW$2EtVk4Hhm1V65GNfYAA5AHj0uyD60f2CmqumqiB/gRk=,email@email.com,firstname,lastname\n+\n+``Importing users with clear-text passwords``\n+---------------------------------------------\n+\n+Clear-text passwords should start with a prefix `cleartext$`. Eg::\n+    \n+    if the password is 'qwerty' it should be encoded as `cleartext$qwerty`.\n+\n+``Autogeneration of usernames and passwords``\n+---------------------------------------------\n+\n+* Email is the only mandatory field of the CSV file. Others important fields like username and password will be auto generated only if omitted.\n+\n+``Sending emails to users``\n+---------------------------","path":"docs/source/general/importing_users.rst","position":69,"original_position":69,"commit_id":"ea7d4e7f8ca6b12639b8aed17984a09c8bf5f5e5","original_commit_id":"ea7d4e7f8ca6b12639b8aed17984a09c8bf5f5e5","user":{"login":"nemesisdesign","id":841044,"node_id":"MDQ6VXNlcjg0MTA0NA==","avatar_url":"https://avatars3.githubusercontent.com/u/841044?v=4","gravatar_id":"","url":"https://api.github.com/users/nemesisdesign","html_url":"https://github.com/nemesisdesign","followers_url":"https://api.github.com/users/nemesisdesign/followers","following_url":"https://api.github.com/users/nemesisdesign/following{/other_user}","gists_url":"https://api.github.com/users/nemesisdesign/gists{/gist_id}","starred_url":"https://api.github.com/users/nemesisdesign/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nemesisdesign/subscriptions","organizations_url":"https://api.github.com/users/nemesisdesign/orgs","repos_url":"https://api.github.com/users/nemesisdesign/repos","events_url":"https://api.github.com/users/nemesisdesign/events{/privacy}","received_events_url":"https://api.github.com/users/nemesisdesign/received_events","type":"User","site_admin":false},"body":"same issue of quotes","created_at":"2018-06-13T09:35:55Z","updated_at":"2018-06-13T09:53:43Z","html_url":"https://github.com/openwisp/django-freeradius/pull/109#discussion_r195016563","pull_request_url":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/openwisp/django-freeradius/pulls/comments/195016563"},"html":{"href":"https://github.com/openwisp/django-freeradius/pull/109#discussion_r195016563"},"pull_request":{"href":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109"}}},"pull_request":{"url":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109","id":174268201,"node_id":"MDExOlB1bGxSZXF1ZXN0MTc0MjY4MjAx","html_url":"https://github.com/openwisp/django-freeradius/pull/109","diff_url":"https://github.com/openwisp/django-freeradius/pull/109.diff","patch_url":"https://github.com/openwisp/django-freeradius/pull/109.patch","issue_url":"https://api.github.com/repos/openwisp/django-freeradius/issues/109","number":109,"state":"open","locked":false,"title":"[feature] Import and generate users using CSV and prefixes","user":{"login":"rohithasrk","id":13820931,"node_id":"MDQ6VXNlcjEzODIwOTMx","avatar_url":"https://avatars1.githubusercontent.com/u/13820931?v=4","gravatar_id":"","url":"https://api.github.com/users/rohithasrk","html_url":"https://github.com/rohithasrk","followers_url":"https://api.github.com/users/rohithasrk/followers","following_url":"https://api.github.com/users/rohithasrk/following{/other_user}","gists_url":"https://api.github.com/users/rohithasrk/gists{/gist_id}","starred_url":"https://api.github.com/users/rohithasrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rohithasrk/subscriptions","organizations_url":"https://api.github.com/users/rohithasrk/orgs","repos_url":"https://api.github.com/users/rohithasrk/repos","events_url":"https://api.github.com/users/rohithasrk/events{/privacy}","received_events_url":"https://api.github.com/users/rohithasrk/received_events","type":"User","site_admin":false},"body":"A basic implementation of the batch add users feature. As of now, it is just a management command which does it. ToDos:\r\n\r\n- [x] Add from the admin interface\r\n- [x] Write unit tests for all the added code\r\n- [x] Discuss the format of the CSV file and edit the handle function accordingly.\r\n- [x] Write documentation that explains how to import users from an existing database\r\n- [x] Write documentation that explains how to create temporary users for events\r\n\r\nImplementation: Created a new model by the name RadiusBatch having m2m relationship with the RadiusCheck model and a parameter named expiration_date(so that it could be used in future) and added a management command to add users from a csv file. I assumed the csv fomat to be\r\n```\r\n    <username> <password>\r\n```\r\nfor now, which could be easily changed. @nemesisdesign Let me know if the method could be improved in any way.","created_at":"2018-03-12T04:03:12Z","updated_at":"2018-06-13T09:53:43Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2973402a740e6a8e449f0416e1f08569e2ce02b0","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109/commits","review_comments_url":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109/comments","review_comment_url":"https://api.github.com/repos/openwisp/django-freeradius/pulls/comments{/number}","comments_url":"https://api.github.com/repos/openwisp/django-freeradius/issues/109/comments","statuses_url":"https://api.github.com/repos/openwisp/django-freeradius/statuses/ea7d4e7f8ca6b12639b8aed17984a09c8bf5f5e5","head":{"label":"rohithasrk:batch-add-users","ref":"batch-add-users","sha":"ea7d4e7f8ca6b12639b8aed17984a09c8bf5f5e5","user":{"login":"rohithasrk","id":13820931,"node_id":"MDQ6VXNlcjEzODIwOTMx","avatar_url":"https://avatars1.githubusercontent.com/u/13820931?v=4","gravatar_id":"","url":"https://api.github.com/users/rohithasrk","html_url":"https://github.com/rohithasrk","followers_url":"https://api.github.com/users/rohithasrk/followers","following_url":"https://api.github.com/users/rohithasrk/following{/other_user}","gists_url":"https://api.github.com/users/rohithasrk/gists{/gist_id}","starred_url":"https://api.github.com/users/rohithasrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rohithasrk/subscriptions","organizations_url":"https://api.github.com/users/rohithasrk/orgs","repos_url":"https://api.github.com/users/rohithasrk/repos","events_url":"https://api.github.com/users/rohithasrk/events{/privacy}","received_events_url":"https://api.github.com/users/rohithasrk/received_events","type":"User","site_admin":false},"repo":{"id":122165661,"node_id":"MDEwOlJlcG9zaXRvcnkxMjIxNjU2NjE=","name":"django-freeradius","full_name":"rohithasrk/django-freeradius","owner":{"login":"rohithasrk","id":13820931,"node_id":"MDQ6VXNlcjEzODIwOTMx","avatar_url":"https://avatars1.githubusercontent.com/u/13820931?v=4","gravatar_id":"","url":"https://api.github.com/users/rohithasrk","html_url":"https://github.com/rohithasrk","followers_url":"https://api.github.com/users/rohithasrk/followers","following_url":"https://api.github.com/users/rohithasrk/following{/other_user}","gists_url":"https://api.github.com/users/rohithasrk/gists{/gist_id}","starred_url":"https://api.github.com/users/rohithasrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rohithasrk/subscriptions","organizations_url":"https://api.github.com/users/rohithasrk/orgs","repos_url":"https://api.github.com/users/rohithasrk/repos","events_url":"https://api.github.com/users/rohithasrk/events{/privacy}","received_events_url":"https://api.github.com/users/rohithasrk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rohithasrk/django-freeradius","description":"Django reusable app that provides an admin interface to a freeradius database (UNRELEASED - work in progress)","fork":true,"url":"https://api.github.com/repos/rohithasrk/django-freeradius","forks_url":"https://api.github.com/repos/rohithasrk/django-freeradius/forks","keys_url":"https://api.github.com/repos/rohithasrk/django-freeradius/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rohithasrk/django-freeradius/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rohithasrk/django-freeradius/teams","hooks_url":"https://api.github.com/repos/rohithasrk/django-freeradius/hooks","issue_events_url":"https://api.github.com/repos/rohithasrk/django-freeradius/issues/events{/number}","events_url":"https://api.github.com/repos/rohithasrk/django-freeradius/events","assignees_url":"https://api.github.com/repos/rohithasrk/django-freeradius/assignees{/user}","branches_url":"https://api.github.com/repos/rohithasrk/django-freeradius/branches{/branch}","tags_url":"https://api.github.com/repos/rohithasrk/django-freeradius/tags","blobs_url":"https://api.github.com/repos/rohithasrk/django-freeradius/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rohithasrk/django-freeradius/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rohithasrk/django-freeradius/git/refs{/sha}","trees_url":"https://api.github.com/repos/rohithasrk/django-freeradius/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rohithasrk/django-freeradius/statuses/{sha}","languages_url":"https://api.github.com/repos/rohithasrk/django-freeradius/languages","stargazers_url":"https://api.github.com/repos/rohithasrk/django-freeradius/stargazers","contributors_url":"https://api.github.com/repos/rohithasrk/django-freeradius/contributors","subscribers_url":"https://api.github.com/repos/rohithasrk/django-freeradius/subscribers","subscription_url":"https://api.github.com/repos/rohithasrk/django-freeradius/subscription","commits_url":"https://api.github.com/repos/rohithasrk/django-freeradius/commits{/sha}","git_commits_url":"https://api.github.com/repos/rohithasrk/django-freeradius/git/commits{/sha}","comments_url":"https://api.github.com/repos/rohithasrk/django-freeradius/comments{/number}","issue_comment_url":"https://api.github.com/repos/rohithasrk/django-freeradius/issues/comments{/number}","contents_url":"https://api.github.com/repos/rohithasrk/django-freeradius/contents/{+path}","compare_url":"https://api.github.com/repos/rohithasrk/django-freeradius/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rohithasrk/django-freeradius/merges","archive_url":"https://api.github.com/repos/rohithasrk/django-freeradius/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rohithasrk/django-freeradius/downloads","issues_url":"https://api.github.com/repos/rohithasrk/django-freeradius/issues{/number}","pulls_url":"https://api.github.com/repos/rohithasrk/django-freeradius/pulls{/number}","milestones_url":"https://api.github.com/repos/rohithasrk/django-freeradius/milestones{/number}","notifications_url":"https://api.github.com/repos/rohithasrk/django-freeradius/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rohithasrk/django-freeradius/labels{/name}","releases_url":"https://api.github.com/repos/rohithasrk/django-freeradius/releases{/id}","deployments_url":"https://api.github.com/repos/rohithasrk/django-freeradius/deployments","created_at":"2018-02-20T07:04:31Z","updated_at":"2018-05-11T17:48:37Z","pushed_at":"2018-06-13T08:29:06Z","git_url":"git://github.com/rohithasrk/django-freeradius.git","ssh_url":"git@github.com:rohithasrk/django-freeradius.git","clone_url":"https://github.com/rohithasrk/django-freeradius.git","svn_url":"https://github.com/rohithasrk/django-freeradius","homepage":"http://django-freeradius.readthedocs.io/","size":262,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"openwisp:master","ref":"master","sha":"a4f4393b2d8c2a763a3ef1519669f657c30ac9c7","user":{"login":"openwisp","id":1150243,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExNTAyNDM=","avatar_url":"https://avatars1.githubusercontent.com/u/1150243?v=4","gravatar_id":"","url":"https://api.github.com/users/openwisp","html_url":"https://github.com/openwisp","followers_url":"https://api.github.com/users/openwisp/followers","following_url":"https://api.github.com/users/openwisp/following{/other_user}","gists_url":"https://api.github.com/users/openwisp/gists{/gist_id}","starred_url":"https://api.github.com/users/openwisp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openwisp/subscriptions","organizations_url":"https://api.github.com/users/openwisp/orgs","repos_url":"https://api.github.com/users/openwisp/repos","events_url":"https://api.github.com/users/openwisp/events{/privacy}","received_events_url":"https://api.github.com/users/openwisp/received_events","type":"Organization","site_admin":false},"repo":{"id":91571033,"node_id":"MDEwOlJlcG9zaXRvcnk5MTU3MTAzMw==","name":"django-freeradius","full_name":"openwisp/django-freeradius","owner":{"login":"openwisp","id":1150243,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExNTAyNDM=","avatar_url":"https://avatars1.githubusercontent.com/u/1150243?v=4","gravatar_id":"","url":"https://api.github.com/users/openwisp","html_url":"https://github.com/openwisp","followers_url":"https://api.github.com/users/openwisp/followers","following_url":"https://api.github.com/users/openwisp/following{/other_user}","gists_url":"https://api.github.com/users/openwisp/gists{/gist_id}","starred_url":"https://api.github.com/users/openwisp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openwisp/subscriptions","organizations_url":"https://api.github.com/users/openwisp/orgs","repos_url":"https://api.github.com/users/openwisp/repos","events_url":"https://api.github.com/users/openwisp/events{/privacy}","received_events_url":"https://api.github.com/users/openwisp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/openwisp/django-freeradius","description":"Django reusable app that provides an admin interface to a freeradius database (UNRELEASED - work in progress)","fork":false,"url":"https://api.github.com/repos/openwisp/django-freeradius","forks_url":"https://api.github.com/repos/openwisp/django-freeradius/forks","keys_url":"https://api.github.com/repos/openwisp/django-freeradius/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openwisp/django-freeradius/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openwisp/django-freeradius/teams","hooks_url":"https://api.github.com/repos/openwisp/django-freeradius/hooks","issue_events_url":"https://api.github.com/repos/openwisp/django-freeradius/issues/events{/number}","events_url":"https://api.github.com/repos/openwisp/django-freeradius/events","assignees_url":"https://api.github.com/repos/openwisp/django-freeradius/assignees{/user}","branches_url":"https://api.github.com/repos/openwisp/django-freeradius/branches{/branch}","tags_url":"https://api.github.com/repos/openwisp/django-freeradius/tags","blobs_url":"https://api.github.com/repos/openwisp/django-freeradius/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openwisp/django-freeradius/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openwisp/django-freeradius/git/refs{/sha}","trees_url":"https://api.github.com/repos/openwisp/django-freeradius/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openwisp/django-freeradius/statuses/{sha}","languages_url":"https://api.github.com/repos/openwisp/django-freeradius/languages","stargazers_url":"https://api.github.com/repos/openwisp/django-freeradius/stargazers","contributors_url":"https://api.github.com/repos/openwisp/django-freeradius/contributors","subscribers_url":"https://api.github.com/repos/openwisp/django-freeradius/subscribers","subscription_url":"https://api.github.com/repos/openwisp/django-freeradius/subscription","commits_url":"https://api.github.com/repos/openwisp/django-freeradius/commits{/sha}","git_commits_url":"https://api.github.com/repos/openwisp/django-freeradius/git/commits{/sha}","comments_url":"https://api.github.com/repos/openwisp/django-freeradius/comments{/number}","issue_comment_url":"https://api.github.com/repos/openwisp/django-freeradius/issues/comments{/number}","contents_url":"https://api.github.com/repos/openwisp/django-freeradius/contents/{+path}","compare_url":"https://api.github.com/repos/openwisp/django-freeradius/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openwisp/django-freeradius/merges","archive_url":"https://api.github.com/repos/openwisp/django-freeradius/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openwisp/django-freeradius/downloads","issues_url":"https://api.github.com/repos/openwisp/django-freeradius/issues{/number}","pulls_url":"https://api.github.com/repos/openwisp/django-freeradius/pulls{/number}","milestones_url":"https://api.github.com/repos/openwisp/django-freeradius/milestones{/number}","notifications_url":"https://api.github.com/repos/openwisp/django-freeradius/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openwisp/django-freeradius/labels{/name}","releases_url":"https://api.github.com/repos/openwisp/django-freeradius/releases{/id}","deployments_url":"https://api.github.com/repos/openwisp/django-freeradius/deployments","created_at":"2017-05-17T11:55:10Z","updated_at":"2018-06-08T11:13:19Z","pushed_at":"2018-06-13T08:29:08Z","git_url":"git://github.com/openwisp/django-freeradius.git","ssh_url":"git@github.com:openwisp/django-freeradius.git","clone_url":"https://github.com/openwisp/django-freeradius.git","svn_url":"https://github.com/openwisp/django-freeradius","homepage":"http://django-freeradius.readthedocs.io/","size":247,"stargazers_count":38,"watchers_count":38,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":26,"mirror_url":null,"archived":false,"open_issues_count":26,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"forks":26,"open_issues":26,"watchers":38,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109"},"html":{"href":"https://github.com/openwisp/django-freeradius/pull/109"},"issue":{"href":"https://api.github.com/repos/openwisp/django-freeradius/issues/109"},"comments":{"href":"https://api.github.com/repos/openwisp/django-freeradius/issues/109/comments"},"review_comments":{"href":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109/comments"},"review_comment":{"href":"https://api.github.com/repos/openwisp/django-freeradius/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/openwisp/django-freeradius/pulls/109/commits"},"statuses":{"href":"https://api.github.com/repos/openwisp/django-freeradius/statuses/ea7d4e7f8ca6b12639b8aed17984a09c8bf5f5e5"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"nemesisdesign"}}