Skip to content

Commit

Permalink
Merge pull request #7 from ministryofjustice/scope-creep
Browse files Browse the repository at this point in the history
Scope creep
  • Loading branch information
Stephen James authored Dec 12, 2023
2 parents c56c107 + 85facce commit 5fdda77
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 50 deletions.
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,48 @@

This repo has been developed by the DevOps Lan&Wi-Fi to automate site creation on juniper mist.

## Run script
## Run script as end user (Assuming you don't have the repo cloned)

Run the following:

1. Copy this in your terminal and paste to create the working directory.

```
mkdir -p ~/mist_working_directory/data_src && cd ~/mist_working_directory
```

2. Copy this in your terminal and paste

```
wget -O .env https://raw.githubusercontent.com/ministryofjustice/main/scope-creep-copy-button/example.env
```

3. Configure .env file:
You must either provide MIST_USERNAME and MIST_PASSWORD or MIST_API_TOKEN. If you opt for username
and password MFA will be requested during runtime. All other inputs are mandatory: ORG_ID, SITE_GROUP_IDS
, RF_TEMPLATE_ID

4. Create a csv file named: `sites_with_clients.csv` within '~/mist_working_directory/data_src'
Below is an [example](./example.sites_with_clients.csv) of how the CSV should be formatted.

```
Site Name,Site Address,Enable GovWifi,Enable MoJWifi, GovWifi Radius Key, Wired NACS Radius Key
Test location 1,"40 Mayflower Dr, Plymouth PL2 3DG", TRUE, FALSE, 00000DD0000BC0EEE000, 00000DD0000BC0EEE000
Test location 2,"102 Petty France, London SW1H 9AJ", FALSE, TRUE, 00000DD0000BC0EEE000, 00000DD0000BC0EEE000
Test location 3,"Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB", FALSE, FALSE, 00000DD0000BC0EEE000, 00000DD0000BC0EEE000
```

or copy the example CSV with the following command to the data directory:

```
wget -O data_src/sites_with_clients.csv https://raw.githubusercontent.com/ministryofjustice/juniper-mist-integration/main/example.sites_with_clients.csv
```

5. Copy this in your terminal and paste to download and run the Dockerized tooling:

```
docker run -it -v $(pwd)/data_src:/data_src --env-file .env ghcr.io/ministryofjustice/nvvs/juniper-mist-integration/app:latest
```

## Development setup

Expand All @@ -16,6 +57,7 @@ This repo has been developed by the DevOps Lan&Wi-Fi to automate site creation o
- Run `make build`
- Integrate built docker container with IDE. [here](https://www.jetbrains.com/help/idea/configuring-remote-python-sdks.html#2546d02c) is the example for intelliJ
- mark src directory & test directory within the IDE. [here](https://www.jetbrains.com/help/idea/content-roots.html)
- Setup environment vars within the IDE. [IntelliJ_docs](https://www.jetbrains.com/help/objc/add-environment-variables-and-program-arguments.html) & [env_file](example.env)

# Notes

Expand Down
7 changes: 7 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
;MIST_USERNAME=
;MIST_PASSWORD=
;MIST_API_TOKEN=
;ORG_ID=
;MIST_API_TOKEN=
;SITE_GROUP_IDS={"moj_wifi": "foo","gov_wifi": "bar"}
;RF_TEMPLATE_ID=
4 changes: 4 additions & 0 deletions example.sites_with_clients.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Site Name,Site Address,Enable GovWifi,Enable MoJWifi, GovWifi Radius Key, Wired NACS Radius Key
Test location 1,"40 Mayflower Dr, Plymouth PL2 3DG", TRUE, FALSE, 00000DD0000BC0EEE000, 00000DD0000BC0EEE000
Test location 2,"102 Petty France, London SW1H 9AJ", FALSE, TRUE, 00000DD0000BC0EEE000, 00000DD0000BC0EEE000
Test location 3,"Met Office, FitzRoy Road, Exeter, Devon, EX1 3PB", FALSE, FALSE, 00000DD0000BC0EEE000, 00000DD0000BC0EEE000
12 changes: 5 additions & 7 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,21 @@ build: ## Build the docker container
docker tag ${IMG} ${LATEST}

.PHONO: create-dir
make create-dir: ## Creates a directory for end user to put CSV file into
make setup-working-directory: ## Setups CSV directory
mkdir data_src;
echo "Please put csv file into data_src then run 'make run-prod'";

.PHONY: run-prod
run-prod: ## Run the python script only mounting the host for csv-file. Format: MIST_API_TOKEN=foo ORG_ID=bar make run-prod
docker run -v $(shell pwd)/data_src:/data_src \
-e MIST_API_TOKEN=$$MIST_API_TOKEN \
-e ORG_ID=$$ORG_ID \
docker run -it -v $(shell pwd)/data_src:/data_src \
--env-file .env \
$(NAME)

.PHONY: run-dev
run-dev: ## Run the python script while mounting the host. This enables using the latest local src code without needing to wait for a container build. Format: MIST_API_TOKEN=foo ORG_ID=bar make run-dev
docker run -v $(shell pwd)/src:/app/src \
docker run -it -v $(shell pwd)/src:/app/src \
-v $(shell pwd)/data_src:/data_src \
-e MIST_API_TOKEN=$$MIST_API_TOKEN \
-e ORG_ID=$$ORG_ID \
--env-file .env \
$(NAME)

.PHONY: tests
Expand Down
105 changes: 80 additions & 25 deletions src/juniper.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,52 @@
import sys
import requests
import json

# Mist CRUD operations


class Admin(object):
def __init__(self, token=''):
self.session = requests.Session()
self.headers = {
'Content-Type': 'application/json',
'Authorization': 'Token ' + token
class Admin:

def login_via_username_and_password(self, username, password):
login_url = self.base_url + "/login"
login_payload = {'email': username, 'password': password}
self.session.post(login_url, data=login_payload)
mfa_headers = {
# Include CSRF token in headers
'X-CSRFToken': self.session.cookies.get('csrftoken.eu'),
}
self.session.headers.update(mfa_headers)
mfa_code = input("Enter MFA:")
login_response = self.session.post(
"https://api.eu.mist.com/api/v1/login/two_factor",
data={"two_factor": mfa_code}
)
if login_response.status_code == 200:
print("Login successful")
else:
raise ValueError("Login was not successful: {response}".format(
response=login_response))

def login_via_token(self, token):
self.headers['Authorization'] = 'Token ' + token
request_url = self.base_url + "/self/apitokens"
responce = self.session.get(request_url, headers=self.headers)
if responce.status_code == 200:
print("Login successful")
else:
raise ValueError(
"Login was not successful via token: {response}".format(response=responce))

def __init__(self, token=None, username=None, password=None):
self.session = requests.Session()
self.headers = {'Content-Type': 'application/json'}
self.base_url = 'https://api.eu.mist.com/api/v1'

if token:
self.login_via_token(token)
elif username and password:
self.login_via_username_and_password(username, password)
else:
raise ValueError("Invalid parameters provided for authentication.")

def post(self, url, payload, timeout=60):
url = 'https://api.eu.mist.com{}'.format(url)
Expand All @@ -27,7 +62,6 @@ def post(self, url, payload, timeout=60):
print('\tPayload: {}'.format(payload))
print('\tResponse: {} ({})'.format(
response.text, response.status_code))

return False

return json.loads(response.text)
Expand All @@ -46,31 +80,46 @@ def put(self, url, payload):
print('\tPayload: {}'.format(payload))
print('\tResponse: {} ({})'.format(
response.text, response.status_code))

return False

return json.loads(response.text)


def check_if_we_need_to_append_gov_wifi_or_moj_wifi_site_groups(gov_wifi, moj_wifi, site_group_ids: dict):
result = []
if moj_wifi == 'TRUE':
result.append(site_group_ids['moj_wifi'])
if gov_wifi == 'TRUE':
result.append(site_group_ids['gov_wifi'])
return result


# Main function
def juniper_script(
data,
mist_api_token='',
org_id=''):
mist_api_token=None,
org_id=None,
mist_username=None,
mist_password=None,
site_group_ids=None,
rf_template_id=None
):

# Configure True/False to enable/disable additional logging of the API response objects
show_more_details = True

# Check for required variables
if mist_api_token == '':
print('Please provide your Mist API token as mist_api_token')
sys.exit(1)
elif org_id == '':
print('Please provide your Mist Organization UUID as org_id')
sys.exit(1)
if org_id is None or org_id == '':
raise ValueError('Please provide Mist org_id')
if (mist_api_token is None) and (mist_username is None or mist_password is None):
raise ValueError(
'No authentication provided, provide mist username and password or API key')
if site_group_ids is None:
raise ValueError('Must provide site_group_ids for GovWifi & MoJWifi')
if rf_template_id is None:
raise ValueError('Must rf_template_id')

# Establish Mist session
admin = Admin(mist_api_token)
admin = Admin(mist_api_token, mist_username, mist_password)

# Create each site from the CSV file
for d in data:
Expand All @@ -80,19 +129,25 @@ def juniper_script(
'address': d.get('Site Address', ''),
"latlng": {"lat": d.get('gps', '')[0], "lng": d.get('gps', '')[1]},
"country_code": d.get('country_code', ''),
"rftemplate_id": rf_template_id,
"timezone": d.get('time_zone', ''),
}
"sitegroup_ids": check_if_we_need_to_append_gov_wifi_or_moj_wifi_site_groups(
gov_wifi=d.get('Enable GovWifi', ''),
moj_wifi=d.get('Enable MoJWifi', ''),
site_group_ids=json.loads(site_group_ids)
),
}

# MOJ specific attributes
site_setting = {

"vars": {
"Enable GovWifi": d.get('Enable GovWifi', ''),
"Enable MoJWifi": d.get('Enable MoJWifi', ''),
"Wired NACS Radius Key": d.get('Wired NACS Radius Key', ''),
"GovWifi Radius Key": d.get('GovWifi Radius Key', '')
"site_specific_radius_wired_nacs_secret": d.get('Wired NACS Radius Key', ''),
"site_specific_radius_govwifi_secret": d.get('GovWifi Radius Key', ''),
"address": d.get('Site Address', ''),
"site_name": d.get('Site Name', '')
},

}

}

Expand Down
8 changes: 6 additions & 2 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ def add_geocoding_to_json(data):
json_data_without_geocoding)

juniper_script(
mist_api_token=os.environ['MIST_API_TOKEN'],
org_id=os.environ['ORG_ID'],
mist_api_token=os.environ.get('MIST_API_TOKEN'),
mist_username=os.environ.get('MIST_USERNAME'),
mist_password=os.environ.get('MIST_PASSWORD'),
site_group_ids=os.environ.get('SITE_GROUP_IDS'),
org_id=os.environ.get('ORG_ID'),
rf_template_id=os.environ.get('RF_TEMPLATE_ID'),
data=json_data_with_geocoding
)
Loading

0 comments on commit 5fdda77

Please sign in to comment.