Import secret feature is now working
This commit is contained in:
		
							parent
							
								
									1af66916a9
								
							
						
					
					
						commit
						6a1429614b
					
				@ -1,6 +1,7 @@
 | 
				
			|||||||
<script setup>
 | 
					<script setup>
 | 
				
			||||||
import CreateSecret from "./components/CreateSecret.vue";
 | 
					import CreateSecret from "./components/CreateSecret.vue";
 | 
				
			||||||
import HomePage from "./components/HomePage.vue";
 | 
					import HomePage from "./components/HomePage.vue";
 | 
				
			||||||
 | 
					import ImportSecrets from "./components/ImportSecrets.vue";
 | 
				
			||||||
import ListSecrets from "./components/ListSecrets.vue";
 | 
					import ListSecrets from "./components/ListSecrets.vue";
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,6 +27,10 @@ import ListSecrets from "./components/ListSecrets.vue";
 | 
				
			|||||||
      <CreateSecret :editSecret="editingSecret" @close="secretSaved" />
 | 
					      <CreateSecret :editSecret="editingSecret" @close="secretSaved" />
 | 
				
			||||||
    </el-dialog>
 | 
					    </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <el-dialog v-model="importDialog" title="Import TOTP secrets" width="80vw">
 | 
				
			||||||
 | 
					      <ImportSecrets />
 | 
				
			||||||
 | 
					    </el-dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="container">
 | 
					    <div class="container">
 | 
				
			||||||
      <div class="timer" v-if="loggedin" :style="{ width: timerWidth + '%' }"></div>
 | 
					      <div class="timer" v-if="loggedin" :style="{ width: timerWidth + '%' }"></div>
 | 
				
			||||||
      <HomePage
 | 
					      <HomePage
 | 
				
			||||||
@ -43,11 +48,13 @@ import ListSecrets from "./components/ListSecrets.vue";
 | 
				
			|||||||
      <ListSecrets :key="listUpdated" v-if="showSecrets && loggedin" @edit="editSecret" />
 | 
					      <ListSecrets :key="listUpdated" v-if="showSecrets && loggedin" @edit="editSecret" />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <button class="create-floating" @click="creationDialog = true">+</button>
 | 
					    <button class="create-floating" @click="creationDialog = true">+</button>
 | 
				
			||||||
 | 
					    <button class="create-floating mb50" @click="importDialog = true">i</button>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
 | 
					  components: { ImportSecrets },
 | 
				
			||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      loggedin: false,
 | 
					      loggedin: false,
 | 
				
			||||||
@ -57,6 +64,7 @@ export default {
 | 
				
			|||||||
      apiBaseUrl: "http://localhost:8000",
 | 
					      apiBaseUrl: "http://localhost:8000",
 | 
				
			||||||
      editDialog: false,
 | 
					      editDialog: false,
 | 
				
			||||||
      editingSecret: {},
 | 
					      editingSecret: {},
 | 
				
			||||||
 | 
					      importDialog: false,
 | 
				
			||||||
      timerWidth: 0,
 | 
					      timerWidth: 0,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -204,4 +212,8 @@ export default {
 | 
				
			|||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  background: none;
 | 
					  background: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.mb50 {
 | 
				
			||||||
 | 
					  margin-bottom: 50px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ export default {
 | 
				
			|||||||
      message: "Hello world!",
 | 
					      message: "Hello world!",
 | 
				
			||||||
      apiBaseUrl: "http://localhost:8000",
 | 
					      apiBaseUrl: "http://localhost:8000",
 | 
				
			||||||
      form: {
 | 
					      form: {
 | 
				
			||||||
        username: "gourav",
 | 
					        username: "gouravkr",
 | 
				
			||||||
        password: "hello123",
 | 
					        password: "hello123",
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										141
									
								
								frontend/src/components/ImportSecrets.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								frontend/src/components/ImportSecrets.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="import-container">
 | 
				
			||||||
 | 
					    <!-- <h3>Select a File to Read</h3> -->
 | 
				
			||||||
 | 
					    <input type="file" id="fileInput" @change="readFile" /> <br />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <button @click="parseTotpUri">Parse tokens</button>
 | 
				
			||||||
 | 
					    <button @click="saveImportedSecrets">Save tokens</button>
 | 
				
			||||||
 | 
					    <div class="token-list">
 | 
				
			||||||
 | 
					      <span v-for="token in parsedTokens" :key="token.issuer">
 | 
				
			||||||
 | 
					        <p v-for="(value, name) in token" :key="name">{{ name }}: {{ value }}</p>
 | 
				
			||||||
 | 
					        <br />
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					      <!-- <span>{{ parsedTokens }}</span> -->
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      apiBaseUrl: "http://localhost:8000",
 | 
				
			||||||
 | 
					      message: "hello",
 | 
				
			||||||
 | 
					      file: null,
 | 
				
			||||||
 | 
					      fileContent: null,
 | 
				
			||||||
 | 
					      allTokens: [],
 | 
				
			||||||
 | 
					      parsedTokens: [],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    parseFile() {
 | 
				
			||||||
 | 
					      var authArr = this.fileContent.split("\r\n");
 | 
				
			||||||
 | 
					      authArr.forEach((ele) => {
 | 
				
			||||||
 | 
					        if (ele.length > 80) {
 | 
				
			||||||
 | 
					          this.allTokens.push(decodeURIComponent(ele));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    readFile(e) {
 | 
				
			||||||
 | 
					      const file = e.target.files[0]; // Get the selected file
 | 
				
			||||||
 | 
					      if (file) {
 | 
				
			||||||
 | 
					        var reader = new FileReader();
 | 
				
			||||||
 | 
					        reader.onload = () => {
 | 
				
			||||||
 | 
					          this.fileContent = reader.result;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        reader.readAsText(file);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        document.getElementById("fileContent").textContent = "No file selected";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    parseTotpUri() {
 | 
				
			||||||
 | 
					      this.parseFile();
 | 
				
			||||||
 | 
					      this.allTokens.forEach((token) => {
 | 
				
			||||||
 | 
					        const url = new URL(token);
 | 
				
			||||||
 | 
					        const scheme = url.protocol.slice(0, -1);
 | 
				
			||||||
 | 
					        var label = decodeURIComponent(url.pathname.slice(1)); // Remove the leading '/'
 | 
				
			||||||
 | 
					        const type = label.slice(1, -1).split("/")[0];
 | 
				
			||||||
 | 
					        label = label.slice(6);
 | 
				
			||||||
 | 
					        let accountName, issuerFromLabel;
 | 
				
			||||||
 | 
					        const labelParts = label.split(":");
 | 
				
			||||||
 | 
					        if (labelParts.length === 2) {
 | 
				
			||||||
 | 
					          issuerFromLabel = labelParts[0];
 | 
				
			||||||
 | 
					          accountName = labelParts[1];
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          accountName = label;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const params = {};
 | 
				
			||||||
 | 
					        url.searchParams.forEach((value, key) => {
 | 
				
			||||||
 | 
					          params[key] = value;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.log(params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const secret = params.secret;
 | 
				
			||||||
 | 
					        const issuer = params.issuer || issuerFromLabel;
 | 
				
			||||||
 | 
					        const algorithm = params.algorithm || "SHA1";
 | 
				
			||||||
 | 
					        const digits = params.digits || "6";
 | 
				
			||||||
 | 
					        const period = params.period || "30";
 | 
				
			||||||
 | 
					        const counter = params.counter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const parts = {
 | 
				
			||||||
 | 
					          scheme,
 | 
				
			||||||
 | 
					          type,
 | 
				
			||||||
 | 
					          label,
 | 
				
			||||||
 | 
					          accountName,
 | 
				
			||||||
 | 
					          issuer,
 | 
				
			||||||
 | 
					          secret,
 | 
				
			||||||
 | 
					          algorithm,
 | 
				
			||||||
 | 
					          digits,
 | 
				
			||||||
 | 
					          period,
 | 
				
			||||||
 | 
					          counter,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.parsedTokens.push(parts);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async createSecret(formData) {
 | 
				
			||||||
 | 
					      const url = `${this.apiBaseUrl}/secret`;
 | 
				
			||||||
 | 
					      const token = sessionStorage.getItem("token");
 | 
				
			||||||
 | 
					      const bodyData = { data: btoa(JSON.stringify(formData)) };
 | 
				
			||||||
 | 
					      const requestOptions = {
 | 
				
			||||||
 | 
					        method: "POST",
 | 
				
			||||||
 | 
					        headers: {
 | 
				
			||||||
 | 
					          "Content-Type": "application/json",
 | 
				
			||||||
 | 
					          Authorization: `Bearer ${token}`,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        body: JSON.stringify(bodyData),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      console.log("this request sent\n", requestOptions);
 | 
				
			||||||
 | 
					      await fetch(url, requestOptions)
 | 
				
			||||||
 | 
					        .then((response) => response.json())
 | 
				
			||||||
 | 
					        .then((data) => console.log(data));
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async saveImportedSecrets() {
 | 
				
			||||||
 | 
					      for (var i = 0; i < this.parsedTokens.length; i++) {
 | 
				
			||||||
 | 
					        await this.createSecret(this.parsedTokens[i]);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.import-container {
 | 
				
			||||||
 | 
					  /* max-height: 60vh; */
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  margin-top: 1vh;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.token-list {
 | 
				
			||||||
 | 
					  height: 60vh;
 | 
				
			||||||
 | 
					  margin-top: 10px;
 | 
				
			||||||
 | 
					  overflow-y: scroll;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user