Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR #9: User Roles created: Enum added, Profile Page Created, #17

Merged
merged 3 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions internal/api/me.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
"fmt"
"net/http"

"github.com/hashicorp-forge/hermes/pkg/models"
"gorm.io/gorm"

gw "github.com/hashicorp-forge/hermes/pkg/googleworkspace"
"github.com/hashicorp/go-hclog"
"google.golang.org/api/people/v1"
Expand All @@ -23,11 +26,17 @@ type MeGetResponse struct {
Picture string `json:"picture"`
Locale string `json:"locale,omitempty"`
HD string `json:"hd,omitempty"`
EmployeeID string `json:"employee_id,omitempty"`
Department string `json:"department,omitempty"`
Organization string `json:"organization,omitempty"`
Profile string `json:"profile,omitempty"`
Role string `json:"role,omitempty"`
}

func MeHandler(
l hclog.Logger,
s *gw.Service,
db *gorm.DB,
) http.Handler {

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -145,6 +154,31 @@ func MeHandler(
FamilyName: p.Names[0].FamilyName,
}

// fetch whether user is admin or not
user := models.User{EmailAddress: p.EmailAddresses[0].Value}
if resp.Role, err = user.FetchRole(db); err != nil {
errResp(
http.StatusInternalServerError,
"Error getting user information",
"error getting user role details",
err,
)
return
}

// Get additional information from user admin api
if err := getOtherUserInfo(
&resp, p, s,
); err != nil {
errResp(
http.StatusInternalServerError,
"Error getting user information",
"error getting additional info with Admin API response",
err,
)
return
}

if len(p.Photos) > 0 {
resp.Picture = p.Photos[0].Url
}
Expand Down Expand Up @@ -194,3 +228,31 @@ func replaceNamesWithAdminAPIResponse(

return nil
}

func getOtherUserInfo(
r *MeGetResponse, p *people.Person, s *gw.Service) error {
if len(p.EmailAddresses) == 0 {
return errors.New("email address not found")
}
u, err := s.GetUser(p.EmailAddresses[0].Value)
if err != nil {
return fmt.Errorf("error getting user: %w", err)
}

for _, adminExtID := range u.ExternalIds.([]interface{}) {
r.EmployeeID = adminExtID.(map[string]interface{})["value"].(string)
break
}

for _, adminOrg := range u.Organizations.([]interface{}) {
r.Organization = adminOrg.(map[string]interface{})["name"].(string)
r.Department = adminOrg.(map[string]interface{})["department"].(string)
r.Profile = adminOrg.(map[string]interface{})["title"].(string)
break
}

// fetch image
r.Picture = u.ThumbnailPhotoUrl

return nil
}
2 changes: 1 addition & 1 deletion internal/cmd/commands/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ func (c *Command) Run(args []string) int {
api.DraftsHandler(cfg, c.Log, algoSearch, algoWrite, goog, db)},
{"/api/v1/drafts/",
api.DraftsDocumentHandler(cfg, c.Log, algoSearch, algoWrite, goog, db)},
{"/api/v1/me", api.MeHandler(c.Log, goog)},
{"/api/v1/me", api.MeHandler(c.Log, goog, db)},
{"/api/v1/me/recently-viewed-docs",
api.MeRecentlyViewedDocsHandler(cfg, c.Log, db)},
{"/api/v1/me/subscriptions",
Expand Down
45 changes: 45 additions & 0 deletions pkg/models/user.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package models

import (
"database/sql/driver"
"errors"
"fmt"
"time"

Expand All @@ -24,6 +26,36 @@ type User struct {

// RecentlyViewedDocs are the documents recently viewed by the user.
RecentlyViewedDocs []Document `gorm:"many2many:recently_viewed_docs;"`

// Role indicates whether the user is an admin or something else, done using an enum.
Role RoleType `gorm:"default:'Basic'"`
}

type RoleType string

const (
Admin RoleType = "Admin"
Basic RoleType = "Basic"
)

func (ct *RoleType) Scan(value interface{}) error {
switch v := value.(type) {
case []byte:
*ct = RoleType(v)
case string:
*ct = RoleType(v)
default:
return fmt.Errorf("unexpected value type for RoleType: %T", value)
}
return nil
}

func (ct RoleType) Value() (driver.Value, error) {
return string(ct), nil
}

func (rt RoleType) String() string {
return string(rt)
}

type RecentlyViewedDoc struct {
Expand Down Expand Up @@ -138,3 +170,16 @@ func (u *User) getAssociations(tx *gorm.DB) error {

return nil
}

// FetchRole gets the value of column "IsAdmin"
func (u *User) FetchRole(db *gorm.DB) (string, error) {
var user User
if err := db.Where("email_address = ?", u.EmailAddress).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return "", nil
}
return "", err
}

return user.Role.String(), nil
}
15 changes: 15 additions & 0 deletions web/app/components/header/nav.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@
"highlighted-new"
}}
/>
<dd.Interactive
data-test-user-menu-item="Your Profile"
@icon = "user"
@route="authenticated.myprofile"
@text="Your Profile"
/>
{{#if (eq this.profile.role "Admin")}}
<dd.Interactive
data-test-user-menu-item="Admin Console"
@icon="settings"
@route="authenticated.myprofile"
@text="Admin Console"
/>
{{/if}}

{{#if this.showSignOut}}
<dd.Interactive
data-test-user-menu-item="sign-out"
Expand Down
12 changes: 10 additions & 2 deletions web/app/controllers/authenticated/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export default class AuthenticatedDashboardController extends Controller {
@tracked TeamName: string = "";
@tracked TeamAbbreviation: string = "";
@tracked TeamIsBeingCreated = false;
@tracked TeamBU: string = "";
@tracked TeamBU: string | null = null;


@action
toggleModal1() {
Expand All @@ -44,6 +45,13 @@ export default class AuthenticatedDashboardController extends Controller {
this.toggleProperty('showModal2');
}


@action protected onProductSelect(
productName: string
) {
this.TeamBU = productName;
}

/**
* Creates a Team, then redirects to the dashboard.
* On error, show a flashMessage and allow users to try again.
Expand Down Expand Up @@ -105,7 +113,7 @@ export default class AuthenticatedDashboardController extends Controller {
// Do something with the form values
this.TeamName = formObject['team-name'];
this.TeamAbbreviation = formObject['team-abbr'];
this.TeamBU = formObject['bu-name'];
// this.TeamBU = formObject['bu-name'];

// now post this info
this.createTeam.perform();
Expand Down
4 changes: 4 additions & 0 deletions web/app/controllers/authenticated/myprofile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Controller from '@ember/controller';

export default class AuthenticatedMyprofileController extends Controller {
}
18 changes: 18 additions & 0 deletions web/app/controllers/authenticated/myprofile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
import AuthenticatedUserService, {
AuthenticatedUser,
} from "hermes/services/authenticated-user";


export default class AuthenticatedMyprofileController extends Controller {
@service declare authenticatedUser: AuthenticatedUserService;

protected get profile(): AuthenticatedUser {
return this.authenticatedUser.info;
}

}


1 change: 1 addition & 0 deletions web/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Router.map(function () {
this.route("new", function () {
this.route("doc");
});
this.route('myprofile');
});
this.route("authenticate");
this.route('404', { path: '/*path' })
Expand Down
4 changes: 4 additions & 0 deletions web/app/routes/authenticated/myprofile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Route from '@ember/routing/route';

export default class AuthenticatedMyprofileRoute extends Route {
}
5 changes: 5 additions & 0 deletions web/app/services/authenticated-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export interface AuthenticatedUser {
email: string;
given_name: string;
picture: string;
employee_id : string ;
department : string ;
organization: string ;
profile : string ;
role : string;
subscriptions: Subscription[];
}

Expand Down
74 changes: 74 additions & 0 deletions web/app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,84 @@
@import "ember-modal-dialog/ember-modal-structure";
@import "ember-modal-dialog/ember-modal-appearance";

// for the dashboard modal dialogue

.ember-modal-dialog{
width: 400px;
}

// for the my profile page
$color_1: #ff6f00;
$background-color_1: #f5f5f5;
$background-color_2: #fff;

.profile-image {
width: 200px;
height: 200px;
border-radius: 50%;
overflow: hidden;
margin: 0 auto 20px;
img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
&:hover {
img {
transform: scale(1.1);
}
}
}


.user-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: $background-color_1;
border-radius: 10px;
padding: 40px;
width: 650px;
margin: auto;
position: relative;
overflow: hidden;
box-shadow: 0 2px 20px -5px rgba(0, 0, 0, 0.5);

&:before {
content: '';
position: absolute;
height: 200%;
width: 250px;
background: #262626;
top: -50px;
left: -175px;
z-index: -1;
transform: rotate(17deg);
}

h2 {
font-size: 24px;
font-weight: bold;
margin-bottom: 20px;
}

p {
margin-bottom: 10px;
}

strong {
font-weight: bold;
}

.badge {
margin-left: 10px;
}
}

// end of myprofile page scss


*,
*::before,
Expand Down
Loading
Loading